Route QCall exceptions through marshalling - #132420

Open
davidwrighton wants to merge 29 commits into
dotnet:mainfrom
davidwrighton:fixup_qcalls
Open

Route QCall exceptions through marshalling#132420
davidwrighton wants to merge 29 commits into
dotnet:mainfrom
davidwrighton:fixup_qcalls

Conversation

@davidwrighton

@davidwrightondavidwrighton commented Aug 17, 2026

Copy link
Copy Markdown
Member

Summary

  • add source-generated LibraryImport support for custom error handlers sourced from the native return value, final parameter, or injected hidden return/parameter locations
  • use the hidden-return error-handler form for CoreCLR QCalls that use BEGIN_QCALL / END_QCALL, while preserving their existing managed signatures
  • change migrated native QCall entry points to return QCallExceptionStatus and move ordinary native return values to explicit out parameters
  • capture exceptions at the QCall boundary and return either a reserved status for preallocated OutOfMemoryException / StackOverflowException instances or a handle to the exact managed exception
  • convert and rethrow the status during managed unmarshalling, preserving the original exception instance and stack trace and releasing exception handles after transfer
  • keep QCalls without the exception-handling macros on their existing ABI, and retain binder-visible helper signatures through managed wrappers where necessary
  • update native declarations and generated browser/WASI call helpers for the new QCall ABI

Testing

  • build.cmd clr+libs -c Checked
  • build.cmd mono+libs
  • build.cmd clr+libs -os browser -c Debug
  • build.cmd clr.corelib -os osx -a x64 -c Debug
  • build.cmd clr.runtime -a x86 -c Checked
  • src\tasks\WasmAppBuilder\generate-coreclr-helpers.cmd -c Debug
  • AmbiguousImplementationException runtime test, including repeated exception propagation
  • RuntimeHelpersTests.QCallExceptionStatus_ThrowsSpecialException
  • RuntimeHelpersTests.QCallExceptionStatus_ThrowsExactExceptionFromHandle
  • COM string marshalling tests covering preservation of the customer-visible exception stack trace
  • migration audit confirming only QCalls using BEGIN_QCALL were converted
  • git diff --check

Note

This pull request description was generated by GitHub Copilot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR changes CoreCLR QCall exception propagation by routing exceptions thrown inside BEGIN_QCALL/END_QCALL through an out QCallException status value that is custom-marshalled back to managed code. The native side captures throwables onto the managed Thread, and the managed marshaller rethrows during unmarshalling, while leaving non-BEGIN_QCALL QCalls on the prior behavior.

Changes:

  • Introduce QCallException + QCallExceptionMarshaller and update managed QCall signatures to include an out QCallException for BEGIN_QCALL-based QCalls.
  • Update native QCall entrypoints and exception macros to capture exceptions instead of dispatching/rethrowing directly across the boundary.
  • Add the per-thread storage needed to temporarily root the captured throwable and rethrow it from managed unmarshalling.

Reviewed changes

Copilot reviewed 170 out of 170 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiFinalizerScheduler.csAdd conditional out QCallException to WASI finalizer QCalls.
src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.csPass conditional out _ to updated reentrant wait QCall.
src/libraries/System.Private.CoreLib/src/System/Threading/Thread.csUpdate CurrentOSThreadId QCall invocation to include conditional out _.
src/libraries/System.Private.CoreLib/src/System/Threading/Lock.csUse updated Environment.GetProcessorCount signature under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.csThread out QCallException through multiple tracing/ALC QCalls.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.csAdd out QCallException to QCalls used by type map cache population/lookups.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCMarshal.csPass out _ to updated ObjC marshal QCalls.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NoCom.csExtend internal signature with conditional out _ under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.csCall updated RegisterIsRootedCallback under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.csUpdate tracing QCall invocations to include out _.
src/libraries/System.Private.CoreLib/src/System/Environment.csUpdate ProcessorCount initialization to use CORECLR QCall signature.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/XplatEventLogger.csAdd out QCallException plumbing to xplat EventSource QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventProvider.csAdd conditional out _ to EventPipeInternal QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.Threads.csAdd conditional out _ to EventPipe session wait/disable QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.csAdd conditional out _ to EventPipe dispatcher QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipe.csAdd conditional out _ to EventPipe enable QCall.
src/libraries/System.Private.CoreLib/src/System/AppContext.csAdd conditional out _ to host property QCall path.
src/coreclr/vm/weakreferencenative.hAdd QCallException* to COM weak ref QCall signatures.
src/coreclr/vm/weakreferencenative.cppAdd QCallException* parameter to COM weak ref QCall implementations.
src/coreclr/vm/wasm/entrypoints.hAdd QCallException* to WASI finalizer exported entrypoints.
src/coreclr/vm/versionresilienthashcode.hAdd QCallException* to version-resilient hash QCall signature.
src/coreclr/vm/versionresilienthashcode.cppAdd QCallException* to version-resilient hash QCall implementation.
src/coreclr/vm/varargsnative.hAdd QCallException* to varargs iterator QCall signatures.
src/coreclr/vm/varargsnative.cppAdd QCallException* to varargs iterator QCall implementations.
src/coreclr/vm/unsafeaccessors.cppAdd QCallException* to unsafe accessor resolver QCall.
src/coreclr/vm/tailcallhelp.hAdd QCallException* to tailcall arg buffer allocation QCall.
src/coreclr/vm/tailcallhelp.cppAdd QCallException* to tailcall arg buffer allocation QCall implementation.
src/coreclr/vm/stubhelpers.hAdd QCallException* to multiple interop/stub helper QCall signatures.
src/coreclr/vm/stackwalk.hAdd QCallException* to AppendExceptionStackFrame QCall signature.
src/coreclr/vm/reflectioninvocation.hAdd QCallException* to reflection invocation QCall signatures.
src/coreclr/vm/qcall.hUpdate QCall guidance/comments and route BEGIN/END macros to capture dispatcher.
src/coreclr/vm/qcall.cppAdd QCallException::SetThrowable implementation and thread rooting logic.
src/coreclr/vm/olevariant.hAdd QCallException* to VARIANT record conversion QCall signature.
src/coreclr/vm/olevariant.cppAdd QCallException* to VARIANT record conversion QCall implementation.
src/coreclr/vm/object.hAdd m_QCallException field + setter on ThreadBaseObject.
src/coreclr/vm/nativelibrarynative.hAdd QCallException* to NativeLibrary load-by-name QCall signature.
src/coreclr/vm/nativelibrarynative.cppAdd QCallException* to NativeLibrary load-by-name QCall implementation.
src/coreclr/vm/nativeeventsource.hAdd QCallException* to EventSource/perf tracing QCall signatures.
src/coreclr/vm/multicorejit.hAdd QCallException* to MultiCoreJIT QCall signatures.
src/coreclr/vm/multicorejit.cppAdd QCallException* to MultiCoreJIT QCall implementations.
src/coreclr/vm/mlinfo.cppAdd QCallException* to StubHelpers_CreateCustomMarshaler QCall implementation.
src/coreclr/vm/method.hppAdd QCallException* to unsafe accessor resolver declaration.
src/coreclr/vm/managedmdimport.hppAdd QCallException* to metadata enum QCall signature.
src/coreclr/vm/managedmdimport.cppAdd QCallException* to metadata enum QCall implementation.
src/coreclr/vm/loaderallocator.hppAdd QCallException* to loader allocator destroy QCall signature.
src/coreclr/vm/loaderallocator.cppAdd QCallException* to loader allocator destroy QCall implementation.
src/coreclr/vm/JitQCallHelpers.hAdd QCallException* to JIT helper QCall signatures.
src/coreclr/vm/jithelpers.cppAdd QCallException* to JIT helper QCalls and adjust handler installation in rare paths.
src/coreclr/vm/interoplibinterface.hAdd QCallException* to ObjC/Java marshal QCall signatures.
src/coreclr/vm/interoplibinterface_objc.cppAdd QCallException* to ObjC marshal QCall implementations.
src/coreclr/vm/interoplibinterface_java.cppAdd QCallException* to Java marshal QCall implementations.
src/coreclr/vm/interoplibinterface_comwrappers.hAdd QCallException* to ComWrappers QCall signatures.
src/coreclr/vm/interoplibinterface_comwrappers.cppAdd QCallException* to ComWrappers QCall implementations.
src/coreclr/vm/ilmarshalers.hAdd QCallException* to safe array marshaler QCall signatures.
src/coreclr/vm/ilmarshalers.cppAdd QCallException* to safe array marshaler QCall implementations.
src/coreclr/vm/finalizerthread.cppAdd QCallException* to WASI finalizer QCall implementations.
src/coreclr/vm/exceptmacros.hIntroduce QCallException native struct and capture-dispatcher exception macros.
src/coreclr/vm/exceptionhandlingqcalls.hAdd QCallException* to exception-handling-related QCall signatures.
src/coreclr/vm/exceptionhandling.cppAdd QCallException* to exception-handling-related QCall implementations.
src/coreclr/vm/excep.cppAdd QCallException* to message QCalls and introduce QCall-specific unwind helper.
src/coreclr/vm/eventpipeinternal.hAdd QCallException* to EventPipeInternal QCall signatures.
src/coreclr/vm/eventpipeinternal.cppAdd QCallException* to EventPipeInternal QCall implementations.
src/coreclr/vm/debugdebugger.hAdd QCallException* to debugger/stacktrace QCall signatures.
src/coreclr/vm/debugdebugger.cppAdd QCallException* to debugger/stacktrace QCall implementations.
src/coreclr/vm/customattribute.hAdd QCallException* to custom attribute QCall signatures.
src/coreclr/vm/customattribute.cppAdd QCallException* to custom attribute QCall implementations.
src/coreclr/vm/corelib.hAdd corelib field mapping for new thread _qcallException field.
src/coreclr/vm/coreassemblyspec.cppAdd QCallException* to assembly spec initialization QCall signature.
src/coreclr/vm/comsynchronizable.hAdd QCallException* to thread/lock-related QCall signatures.
src/coreclr/vm/comsynchronizable.cppAdd QCallException* to thread/lock-related QCall implementations.
src/coreclr/vm/commodule.hAdd QCallException* to Reflection.Emit module builder QCall signatures.
src/coreclr/vm/commodule.cppAdd QCallException* to Reflection.Emit module builder QCall implementations.
src/coreclr/vm/comdependenthandle.hAdd QCallException* to dependent handle QCall signatures.
src/coreclr/vm/comdependenthandle.cppAdd QCallException* to dependent handle QCall implementations.
src/coreclr/vm/comdelegate.hAdd QCallException* to delegate-related QCall signatures.
src/coreclr/vm/comdelegate.cppAdd QCallException* to delegate-related QCall implementations.
src/coreclr/vm/clrconfignative.hAdd QCallException* to CLR config QCall signature.
src/coreclr/vm/clrconfignative.cppAdd QCallException* to CLR config QCall implementation.
src/coreclr/vm/arraynative.hAdd QCallException* to array creation/ctor QCall signatures.
src/coreclr/vm/arraynative.cppAdd QCallException* to array creation/ctor QCall implementations.
src/coreclr/vm/appdomainnative.hppAdd QCallException* to AppDomain/AppContext/string intern QCall signatures.
src/coreclr/vm/appdomainnative.cppAdd QCallException* to AppDomain/AppContext/string intern QCall implementations.
src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csprojInclude new QCallException.cs in CoreCLR CoreLib build.
src/coreclr/System.Private.CoreLib/src/System/Variant.csAdd out QCallException to COM VARIANT-related QCalls.
src/coreclr/System.Private.CoreLib/src/System/ValueType.csAdd out QCallException to ValueType helper QCalls.
src/coreclr/System.Private.CoreLib/src/System/TypeLoadException.CoreCLR.csAdd out QCallException to message-formatting QCall.
src/coreclr/System.Private.CoreLib/src/System/Threading/ObjectHeader.CoreCLR.csAdd out QCallException to lock-object QCall.
src/coreclr/System.Private.CoreLib/src/System/String.CoreCLR.csAdd out QCallException to string intern/is-interned QCalls.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CreateUninitializedCache.CoreCLR.csAdd out QCallException to create-uninitialized info QCall.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.csAdd out QCallException to multiple RuntimeType/reflection QCalls.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.BoxCache.csAdd out QCallException to boxing cache QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/TrackerObjectManager.CoreCLR.csAdd out QCallException to wrapper-cache registration QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.CoreCLR.csAdd out QCallException to ObjC marshal QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.CoreCLR.csAdd out QCallException to NativeLibrary load QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/JavaMarshal.CoreCLR.csAdd out QCallException to Java marshal QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.csAdd out QCallException to GCHandle QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.CoreCLR.csAdd out QCallException to ComWrappers QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.csAdd out QCallException to EH-related QCall internal calls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/DependentHandle.csAdd out QCallException to dependent handle QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/ControlledExecution.CoreCLR.csAdd out QCallException to ThreadNative_Abort QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/VirtualDispatchHelpers.csAdd out QCallException to virtual dispatch resolver QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/StaticsHelpers.csAdd out QCallException to thread statics QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallException.csIntroduce managed QCall exception status type + custom marshaller.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/InitHelpers.csAdd out QCallException to init-class QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/GenericsHelpers.csAdd out QCallException to generic handle worker QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.csAdd out QCallException to cast helper QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.csAdd out QCallException to async continuation QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.CoreCLR.csAdd out QCallException to unsafe accessor resolver QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.csAdd out QCallException to module name/types QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.csAdd out QCallException to custom attribute QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.csAdd out QCallException to GetCurrentMethod QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.csAdd out QCallException to metadata update QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.csAdd out QCallException to raw metadata QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.csAdd out QCallException to metadata import enum QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/LoaderAllocator.csAdd out QCallException to loader allocator destroy QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeParameterBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.csAdd out QCallException to dynamic assembly creation QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.CoreCLR.csAdd out _ to compile-method QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.csAdd out _ to destroy handle QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/Reflection/AssemblyName.CoreCLR.csAdd out QCallException to assembly spec init QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.csAdd out QCallException to assembly lookup QCalls.
src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.csAdd out _ to clone allocation QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/IO/Stream.CoreCLR.csAdd out QCallException to overridden-check QCall.
src/coreclr/System.Private.CoreLib/src/System/IO/FileLoadException.CoreCLR.csAdd out QCallException to file load message QCalls.
src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.csAdd out QCallException to exception-related QCalls.
src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.csAdd out QCallException to Environment QCalls (Exit/FailFast/GetProcessorCount).
src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.csAdd out QCallException to enum values/names QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreCLR.csAdd out QCallException to stack trace frames QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrame.CoreCLR.csAdd out QCallException to method-desc-from-IP QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Debugger.csAdd out QCallException to debugger break/notification QCalls.
src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.csAdd out QCallException to delegate binding/construct QCalls.
src/coreclr/System.Private.CoreLib/src/System/ComAwareWeakReference.CoreCLR.csAdd out QCallException to COM weak ref QCalls.
src/coreclr/System.Private.CoreLib/src/System/CLRConfig.csAdd out QCallException to CLR config QCall.
src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.csAdd out QCallException to array creation/ctor QCalls.
src/coreclr/System.Private.CoreLib/src/System/ArgIterator.csAdd out QCallException to varargs iterator QCalls.
src/coreclr/System.Private.CoreLib/src/System/AppContext.CoreCLR.csAdd out QCallException to host property QCall.
src/coreclr/System.Private.CoreLib/src/Internal/VersionResilientHashCode.CoreCLR.csAdd out QCallException to version-resilient hash QCall.
src/coreclr/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.csAdd out QCallException to throw-helper QCalls.
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.csAdd conditional out _ to EH QCall internal calls.
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.csAdd conditional out _ to EH QCall internal calls.

Comment threadsrc/coreclr/vm/exceptmacros.h
@azure-pipelines

Copy link
Copy Markdown
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.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
CopilotAI review requested due to automatic review settings August 18, 2026 00:15

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 170 out of 170 changed files in this pull request and generated 1 comment.

CopilotAI review requested due to automatic review settings August 18, 2026 19:45

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 171 out of 171 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallException.cs:26

  • QCallExceptionMarshaller.FromUnmanaged only treats exceptionPending == 1 as an exception. Since the native representation is an INT32 flag, using a non-zero check (!= 0) is more robust against future expansion or accidental non-1 values, and avoids silently skipping exception propagation if the flag is ever set to another non-zero value.

Add managed forwarding overloads for helpers invoked by native IL stub generation so their CoreLib binder signatures remain stable after adding QCall exception parameters.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
Clean up accidental formatting and redundant conditionals introduced by the QCall migration. Make the COM stack trace assertion tolerate non-frame and additional lines while preserving frame-order validation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 31769fe8-86a0-4379-91f3-5353097eb47c
CopilotAI review requested due to automatic review settings September 1, 2026 19:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs:794

  • GetMethod(...) can return null, which would make failures here surface as a NullReferenceException later (or even pass silently if callers change). Since this helper is only for tests, it’s better to assert the MethodInfo is found so failures are clear and localized.
    src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallExceptionStatus.cs:45
  • The <= 2 check is a protocol constant that’s easy to lose track of when the native side evolves. Consider naming it as a constant so the intent is explicit and future updates are harder to miss.
  • Files reviewed: 155/155 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment threadsrc/tests/Interop/COM/NETClients/Primitives/StringTests.cs
Restore normal native QCall return values and pass exception status through a hidden final parameter.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 02:19

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It changes CoreCLR’s QCall exception-handling ABI and dispatch machinery across a wide set of managed and native entrypoints, making the regression surface too large to approve without focused human validation.

Review details
  • Files reviewed: 155/155 changed files
  • Comments generated: 1
  • Review effort level: Lite

@jkotas

jkotas commented Sep 2, 2026

Copy link
Copy Markdown
Member

Restore normal native QCall return values and pass exception status through a hidden final parameter.

Was there a specific problem with HiddenReturnValue that made you switch to HiddenLastParameter?

Comment threadsrc/coreclr/vm/excep.cpp Outdated
Comment threadsrc/coreclr/vm/exceptionhandling.cpp Outdated
Avoid relying on GET_THREAD and explicit MAKE_CURRENT_THREAD_AVAILABLE calls in QCall exception and reflection paths. Retrieve the current thread directly with GetThread, and correct the QCall casing in the exception rethrow helper name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 21:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The generated WASM callhelpers tables appear to drop the CompressionNative_CompressBound entry while managed code still imports it, which risks runtime entrypoint resolution failures on browser/WASI.

Review details

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallExceptionStatus.cs:45

  • QCallExceptionStatusMarshaller treats status values 1 and 2 as special cases, but the code currently uses a hard-coded magic number ("<= 2"). This makes the reserved-status contract harder to maintain and audit.

Consider introducing named constants (e.g., QCallOutOfMemoryExceptionStatus = 1, QCallStackOverflowExceptionStatus = 2, and/or MaxSpecialExceptionStatus = 2) and using them in the comparison.

src/coreclr/vm/wasm/browser/callhelpers-pinvoke.cpp:22

  • CompressionNative_CompressBound was removed from the generated WASM callhelpers P/Invoke table, but managed code still declares a [LibraryImport] for it (Interop.ZLib.compressBound). If this entry is missing at runtime, CompressionNative_CompressBound resolution is likely to fail (EntryPointNotFound / missing symbol) on browser/WASI.

Please ensure the callhelpers generator includes CompressionNative_CompressBound in both browser and WASI outputs (and the table counts match).

  • Files reviewed: 155/155 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Keep the CompressionNative_CompressBound entry in the browser and WASI static P/Invoke tables and update their entry counts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 21:58

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The marshaller’s special-status handling can preserve stale OutOfMemoryException stack traces due to reuse of the preallocated instance, which should be corrected before merging.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 155/155 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment on lines +41 to +61
Exception exception;
if ((nuint)status <= 2)
{
exception = Thread.GetQCallSpecialException(status);
}
else
{
GCHandle exceptionHandle = GCHandle.FromIntPtr(status);
try
{
exception = (Exception)exceptionHandle.Target!;
}
finally
{
exceptionHandle.Free();
}
}

// Throw during unmarshalling so QCall exception propagation remains as close as
// possible to throwing directly from native code, as QCalls did previously.
ExceptionDispatchInfo.Throw(exception);
Comment on lines +60 to +65
internal static Exception GetQCallSpecialException(nint status)
{
Exception? exception = null;
GetQCallSpecialException(status, ObjectHandleOnStack.Create(ref exception));
return exception!;
}
// [DllImport(JitHelpers.QCall, EntryPoint = "FooNative_Bar", CharSet = CharSet.Unicode)]
// // QCalls should always be static extern.
// private static extern bool Bar(int flags, string inString, StringHandleOnStack retString);
// // All QCalls that use BEGIN_QCALL must use the hidden last parameter error handler.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also described in docs\design\coreclr\botr\corelib.md . Can we update that copy as well? (Or deduplicate the description by replacing one of the copies with a link.)

{
"System.StubHelpers.StubHelpers.ThrowInteropParamException",
"Server.Contract.IStringTesting.Reverse_LPWStr_OutAttr",
"Xunit.Assert.RecordException",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is likely to be unreliable with JIT stress when some of the methods can be aggressively inlined and no longer show up in the stacktrace.

@jkotas

Copy link
Copy Markdown
Member

LGTM otherwise

internal static unsafe partial class ThrowHelpers
{
[DoesNotReturn]
[ErrorHandler(typeof(QCallExceptionStatusMarshaller), ErrorLocation.HiddenLastParameter)]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Reposting earlier question since it got lost in the comment noise)

Was there a specific problem with HiddenReturnValue that made you switch to HiddenLastParameter?

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@davidwrighton@jkotas@AustinWise
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Route QCall exceptions through marshalling - #132420

Open
davidwrighton wants to merge 29 commits into
dotnet:mainfrom
davidwrighton:fixup_qcalls
Open

Route QCall exceptions through marshalling#132420
davidwrighton wants to merge 29 commits into
dotnet:mainfrom
davidwrighton:fixup_qcalls

Conversation

@davidwrighton

@davidwrightondavidwrighton commented Aug 17, 2026

Copy link
Copy Markdown
Member

Summary

  • add source-generated LibraryImport support for custom error handlers sourced from the native return value, final parameter, or injected hidden return/parameter locations
  • use the hidden-return error-handler form for CoreCLR QCalls that use BEGIN_QCALL / END_QCALL, while preserving their existing managed signatures
  • change migrated native QCall entry points to return QCallExceptionStatus and move ordinary native return values to explicit out parameters
  • capture exceptions at the QCall boundary and return either a reserved status for preallocated OutOfMemoryException / StackOverflowException instances or a handle to the exact managed exception
  • convert and rethrow the status during managed unmarshalling, preserving the original exception instance and stack trace and releasing exception handles after transfer
  • keep QCalls without the exception-handling macros on their existing ABI, and retain binder-visible helper signatures through managed wrappers where necessary
  • update native declarations and generated browser/WASI call helpers for the new QCall ABI

Testing

  • build.cmd clr+libs -c Checked
  • build.cmd mono+libs
  • build.cmd clr+libs -os browser -c Debug
  • build.cmd clr.corelib -os osx -a x64 -c Debug
  • build.cmd clr.runtime -a x86 -c Checked
  • src\tasks\WasmAppBuilder\generate-coreclr-helpers.cmd -c Debug
  • AmbiguousImplementationException runtime test, including repeated exception propagation
  • RuntimeHelpersTests.QCallExceptionStatus_ThrowsSpecialException
  • RuntimeHelpersTests.QCallExceptionStatus_ThrowsExactExceptionFromHandle
  • COM string marshalling tests covering preservation of the customer-visible exception stack trace
  • migration audit confirming only QCalls using BEGIN_QCALL were converted
  • git diff --check

Note

This pull request description was generated by GitHub Copilot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR changes CoreCLR QCall exception propagation by routing exceptions thrown inside BEGIN_QCALL/END_QCALL through an out QCallException status value that is custom-marshalled back to managed code. The native side captures throwables onto the managed Thread, and the managed marshaller rethrows during unmarshalling, while leaving non-BEGIN_QCALL QCalls on the prior behavior.

Changes:

  • Introduce QCallException + QCallExceptionMarshaller and update managed QCall signatures to include an out QCallException for BEGIN_QCALL-based QCalls.
  • Update native QCall entrypoints and exception macros to capture exceptions instead of dispatching/rethrowing directly across the boundary.
  • Add the per-thread storage needed to temporarily root the captured throwable and rethrow it from managed unmarshalling.

Reviewed changes

Copilot reviewed 170 out of 170 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiFinalizerScheduler.csAdd conditional out QCallException to WASI finalizer QCalls.
src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.csPass conditional out _ to updated reentrant wait QCall.
src/libraries/System.Private.CoreLib/src/System/Threading/Thread.csUpdate CurrentOSThreadId QCall invocation to include conditional out _.
src/libraries/System.Private.CoreLib/src/System/Threading/Lock.csUse updated Environment.GetProcessorCount signature under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.csThread out QCallException through multiple tracing/ALC QCalls.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.csAdd out QCallException to QCalls used by type map cache population/lookups.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCMarshal.csPass out _ to updated ObjC marshal QCalls.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NoCom.csExtend internal signature with conditional out _ under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.csCall updated RegisterIsRootedCallback under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.csUpdate tracing QCall invocations to include out _.
src/libraries/System.Private.CoreLib/src/System/Environment.csUpdate ProcessorCount initialization to use CORECLR QCall signature.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/XplatEventLogger.csAdd out QCallException plumbing to xplat EventSource QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventProvider.csAdd conditional out _ to EventPipeInternal QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.Threads.csAdd conditional out _ to EventPipe session wait/disable QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.csAdd conditional out _ to EventPipe dispatcher QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipe.csAdd conditional out _ to EventPipe enable QCall.
src/libraries/System.Private.CoreLib/src/System/AppContext.csAdd conditional out _ to host property QCall path.
src/coreclr/vm/weakreferencenative.hAdd QCallException* to COM weak ref QCall signatures.
src/coreclr/vm/weakreferencenative.cppAdd QCallException* parameter to COM weak ref QCall implementations.
src/coreclr/vm/wasm/entrypoints.hAdd QCallException* to WASI finalizer exported entrypoints.
src/coreclr/vm/versionresilienthashcode.hAdd QCallException* to version-resilient hash QCall signature.
src/coreclr/vm/versionresilienthashcode.cppAdd QCallException* to version-resilient hash QCall implementation.
src/coreclr/vm/varargsnative.hAdd QCallException* to varargs iterator QCall signatures.
src/coreclr/vm/varargsnative.cppAdd QCallException* to varargs iterator QCall implementations.
src/coreclr/vm/unsafeaccessors.cppAdd QCallException* to unsafe accessor resolver QCall.
src/coreclr/vm/tailcallhelp.hAdd QCallException* to tailcall arg buffer allocation QCall.
src/coreclr/vm/tailcallhelp.cppAdd QCallException* to tailcall arg buffer allocation QCall implementation.
src/coreclr/vm/stubhelpers.hAdd QCallException* to multiple interop/stub helper QCall signatures.
src/coreclr/vm/stackwalk.hAdd QCallException* to AppendExceptionStackFrame QCall signature.
src/coreclr/vm/reflectioninvocation.hAdd QCallException* to reflection invocation QCall signatures.
src/coreclr/vm/qcall.hUpdate QCall guidance/comments and route BEGIN/END macros to capture dispatcher.
src/coreclr/vm/qcall.cppAdd QCallException::SetThrowable implementation and thread rooting logic.
src/coreclr/vm/olevariant.hAdd QCallException* to VARIANT record conversion QCall signature.
src/coreclr/vm/olevariant.cppAdd QCallException* to VARIANT record conversion QCall implementation.
src/coreclr/vm/object.hAdd m_QCallException field + setter on ThreadBaseObject.
src/coreclr/vm/nativelibrarynative.hAdd QCallException* to NativeLibrary load-by-name QCall signature.
src/coreclr/vm/nativelibrarynative.cppAdd QCallException* to NativeLibrary load-by-name QCall implementation.
src/coreclr/vm/nativeeventsource.hAdd QCallException* to EventSource/perf tracing QCall signatures.
src/coreclr/vm/multicorejit.hAdd QCallException* to MultiCoreJIT QCall signatures.
src/coreclr/vm/multicorejit.cppAdd QCallException* to MultiCoreJIT QCall implementations.
src/coreclr/vm/mlinfo.cppAdd QCallException* to StubHelpers_CreateCustomMarshaler QCall implementation.
src/coreclr/vm/method.hppAdd QCallException* to unsafe accessor resolver declaration.
src/coreclr/vm/managedmdimport.hppAdd QCallException* to metadata enum QCall signature.
src/coreclr/vm/managedmdimport.cppAdd QCallException* to metadata enum QCall implementation.
src/coreclr/vm/loaderallocator.hppAdd QCallException* to loader allocator destroy QCall signature.
src/coreclr/vm/loaderallocator.cppAdd QCallException* to loader allocator destroy QCall implementation.
src/coreclr/vm/JitQCallHelpers.hAdd QCallException* to JIT helper QCall signatures.
src/coreclr/vm/jithelpers.cppAdd QCallException* to JIT helper QCalls and adjust handler installation in rare paths.
src/coreclr/vm/interoplibinterface.hAdd QCallException* to ObjC/Java marshal QCall signatures.
src/coreclr/vm/interoplibinterface_objc.cppAdd QCallException* to ObjC marshal QCall implementations.
src/coreclr/vm/interoplibinterface_java.cppAdd QCallException* to Java marshal QCall implementations.
src/coreclr/vm/interoplibinterface_comwrappers.hAdd QCallException* to ComWrappers QCall signatures.
src/coreclr/vm/interoplibinterface_comwrappers.cppAdd QCallException* to ComWrappers QCall implementations.
src/coreclr/vm/ilmarshalers.hAdd QCallException* to safe array marshaler QCall signatures.
src/coreclr/vm/ilmarshalers.cppAdd QCallException* to safe array marshaler QCall implementations.
src/coreclr/vm/finalizerthread.cppAdd QCallException* to WASI finalizer QCall implementations.
src/coreclr/vm/exceptmacros.hIntroduce QCallException native struct and capture-dispatcher exception macros.
src/coreclr/vm/exceptionhandlingqcalls.hAdd QCallException* to exception-handling-related QCall signatures.
src/coreclr/vm/exceptionhandling.cppAdd QCallException* to exception-handling-related QCall implementations.
src/coreclr/vm/excep.cppAdd QCallException* to message QCalls and introduce QCall-specific unwind helper.
src/coreclr/vm/eventpipeinternal.hAdd QCallException* to EventPipeInternal QCall signatures.
src/coreclr/vm/eventpipeinternal.cppAdd QCallException* to EventPipeInternal QCall implementations.
src/coreclr/vm/debugdebugger.hAdd QCallException* to debugger/stacktrace QCall signatures.
src/coreclr/vm/debugdebugger.cppAdd QCallException* to debugger/stacktrace QCall implementations.
src/coreclr/vm/customattribute.hAdd QCallException* to custom attribute QCall signatures.
src/coreclr/vm/customattribute.cppAdd QCallException* to custom attribute QCall implementations.
src/coreclr/vm/corelib.hAdd corelib field mapping for new thread _qcallException field.
src/coreclr/vm/coreassemblyspec.cppAdd QCallException* to assembly spec initialization QCall signature.
src/coreclr/vm/comsynchronizable.hAdd QCallException* to thread/lock-related QCall signatures.
src/coreclr/vm/comsynchronizable.cppAdd QCallException* to thread/lock-related QCall implementations.
src/coreclr/vm/commodule.hAdd QCallException* to Reflection.Emit module builder QCall signatures.
src/coreclr/vm/commodule.cppAdd QCallException* to Reflection.Emit module builder QCall implementations.
src/coreclr/vm/comdependenthandle.hAdd QCallException* to dependent handle QCall signatures.
src/coreclr/vm/comdependenthandle.cppAdd QCallException* to dependent handle QCall implementations.
src/coreclr/vm/comdelegate.hAdd QCallException* to delegate-related QCall signatures.
src/coreclr/vm/comdelegate.cppAdd QCallException* to delegate-related QCall implementations.
src/coreclr/vm/clrconfignative.hAdd QCallException* to CLR config QCall signature.
src/coreclr/vm/clrconfignative.cppAdd QCallException* to CLR config QCall implementation.
src/coreclr/vm/arraynative.hAdd QCallException* to array creation/ctor QCall signatures.
src/coreclr/vm/arraynative.cppAdd QCallException* to array creation/ctor QCall implementations.
src/coreclr/vm/appdomainnative.hppAdd QCallException* to AppDomain/AppContext/string intern QCall signatures.
src/coreclr/vm/appdomainnative.cppAdd QCallException* to AppDomain/AppContext/string intern QCall implementations.
src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csprojInclude new QCallException.cs in CoreCLR CoreLib build.
src/coreclr/System.Private.CoreLib/src/System/Variant.csAdd out QCallException to COM VARIANT-related QCalls.
src/coreclr/System.Private.CoreLib/src/System/ValueType.csAdd out QCallException to ValueType helper QCalls.
src/coreclr/System.Private.CoreLib/src/System/TypeLoadException.CoreCLR.csAdd out QCallException to message-formatting QCall.
src/coreclr/System.Private.CoreLib/src/System/Threading/ObjectHeader.CoreCLR.csAdd out QCallException to lock-object QCall.
src/coreclr/System.Private.CoreLib/src/System/String.CoreCLR.csAdd out QCallException to string intern/is-interned QCalls.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CreateUninitializedCache.CoreCLR.csAdd out QCallException to create-uninitialized info QCall.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.csAdd out QCallException to multiple RuntimeType/reflection QCalls.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.BoxCache.csAdd out QCallException to boxing cache QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/TrackerObjectManager.CoreCLR.csAdd out QCallException to wrapper-cache registration QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.CoreCLR.csAdd out QCallException to ObjC marshal QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.CoreCLR.csAdd out QCallException to NativeLibrary load QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/JavaMarshal.CoreCLR.csAdd out QCallException to Java marshal QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.csAdd out QCallException to GCHandle QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.CoreCLR.csAdd out QCallException to ComWrappers QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.csAdd out QCallException to EH-related QCall internal calls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/DependentHandle.csAdd out QCallException to dependent handle QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/ControlledExecution.CoreCLR.csAdd out QCallException to ThreadNative_Abort QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/VirtualDispatchHelpers.csAdd out QCallException to virtual dispatch resolver QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/StaticsHelpers.csAdd out QCallException to thread statics QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallException.csIntroduce managed QCall exception status type + custom marshaller.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/InitHelpers.csAdd out QCallException to init-class QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/GenericsHelpers.csAdd out QCallException to generic handle worker QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.csAdd out QCallException to cast helper QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.csAdd out QCallException to async continuation QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.CoreCLR.csAdd out QCallException to unsafe accessor resolver QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.csAdd out QCallException to module name/types QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.csAdd out QCallException to custom attribute QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.csAdd out QCallException to GetCurrentMethod QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.csAdd out QCallException to metadata update QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.csAdd out QCallException to raw metadata QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.csAdd out QCallException to metadata import enum QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/LoaderAllocator.csAdd out QCallException to loader allocator destroy QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeParameterBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.csAdd out QCallException to dynamic assembly creation QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.CoreCLR.csAdd out _ to compile-method QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.csAdd out _ to destroy handle QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/Reflection/AssemblyName.CoreCLR.csAdd out QCallException to assembly spec init QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.csAdd out QCallException to assembly lookup QCalls.
src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.csAdd out _ to clone allocation QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/IO/Stream.CoreCLR.csAdd out QCallException to overridden-check QCall.
src/coreclr/System.Private.CoreLib/src/System/IO/FileLoadException.CoreCLR.csAdd out QCallException to file load message QCalls.
src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.csAdd out QCallException to exception-related QCalls.
src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.csAdd out QCallException to Environment QCalls (Exit/FailFast/GetProcessorCount).
src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.csAdd out QCallException to enum values/names QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreCLR.csAdd out QCallException to stack trace frames QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrame.CoreCLR.csAdd out QCallException to method-desc-from-IP QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Debugger.csAdd out QCallException to debugger break/notification QCalls.
src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.csAdd out QCallException to delegate binding/construct QCalls.
src/coreclr/System.Private.CoreLib/src/System/ComAwareWeakReference.CoreCLR.csAdd out QCallException to COM weak ref QCalls.
src/coreclr/System.Private.CoreLib/src/System/CLRConfig.csAdd out QCallException to CLR config QCall.
src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.csAdd out QCallException to array creation/ctor QCalls.
src/coreclr/System.Private.CoreLib/src/System/ArgIterator.csAdd out QCallException to varargs iterator QCalls.
src/coreclr/System.Private.CoreLib/src/System/AppContext.CoreCLR.csAdd out QCallException to host property QCall.
src/coreclr/System.Private.CoreLib/src/Internal/VersionResilientHashCode.CoreCLR.csAdd out QCallException to version-resilient hash QCall.
src/coreclr/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.csAdd out QCallException to throw-helper QCalls.
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.csAdd conditional out _ to EH QCall internal calls.
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.csAdd conditional out _ to EH QCall internal calls.

Comment threadsrc/coreclr/vm/exceptmacros.h
@azure-pipelines

Copy link
Copy Markdown
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.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
CopilotAI review requested due to automatic review settings August 18, 2026 00:15

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 170 out of 170 changed files in this pull request and generated 1 comment.

CopilotAI review requested due to automatic review settings August 18, 2026 19:45

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 171 out of 171 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallException.cs:26

  • QCallExceptionMarshaller.FromUnmanaged only treats exceptionPending == 1 as an exception. Since the native representation is an INT32 flag, using a non-zero check (!= 0) is more robust against future expansion or accidental non-1 values, and avoids silently skipping exception propagation if the flag is ever set to another non-zero value.

Add managed forwarding overloads for helpers invoked by native IL stub generation so their CoreLib binder signatures remain stable after adding QCall exception parameters.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
Clean up accidental formatting and redundant conditionals introduced by the QCall migration. Make the COM stack trace assertion tolerate non-frame and additional lines while preserving frame-order validation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 31769fe8-86a0-4379-91f3-5353097eb47c
CopilotAI review requested due to automatic review settings September 1, 2026 19:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs:794

  • GetMethod(...) can return null, which would make failures here surface as a NullReferenceException later (or even pass silently if callers change). Since this helper is only for tests, it’s better to assert the MethodInfo is found so failures are clear and localized.
    src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallExceptionStatus.cs:45
  • The <= 2 check is a protocol constant that’s easy to lose track of when the native side evolves. Consider naming it as a constant so the intent is explicit and future updates are harder to miss.
  • Files reviewed: 155/155 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment threadsrc/tests/Interop/COM/NETClients/Primitives/StringTests.cs
Restore normal native QCall return values and pass exception status through a hidden final parameter.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 02:19

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It changes CoreCLR’s QCall exception-handling ABI and dispatch machinery across a wide set of managed and native entrypoints, making the regression surface too large to approve without focused human validation.

Review details
  • Files reviewed: 155/155 changed files
  • Comments generated: 1
  • Review effort level: Lite

@jkotas

jkotas commented Sep 2, 2026

Copy link
Copy Markdown
Member

Restore normal native QCall return values and pass exception status through a hidden final parameter.

Was there a specific problem with HiddenReturnValue that made you switch to HiddenLastParameter?

Comment threadsrc/coreclr/vm/excep.cpp Outdated
Comment threadsrc/coreclr/vm/exceptionhandling.cpp Outdated
Avoid relying on GET_THREAD and explicit MAKE_CURRENT_THREAD_AVAILABLE calls in QCall exception and reflection paths. Retrieve the current thread directly with GetThread, and correct the QCall casing in the exception rethrow helper name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 21:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The generated WASM callhelpers tables appear to drop the CompressionNative_CompressBound entry while managed code still imports it, which risks runtime entrypoint resolution failures on browser/WASI.

Review details

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallExceptionStatus.cs:45

  • QCallExceptionStatusMarshaller treats status values 1 and 2 as special cases, but the code currently uses a hard-coded magic number ("<= 2"). This makes the reserved-status contract harder to maintain and audit.

Consider introducing named constants (e.g., QCallOutOfMemoryExceptionStatus = 1, QCallStackOverflowExceptionStatus = 2, and/or MaxSpecialExceptionStatus = 2) and using them in the comparison.

src/coreclr/vm/wasm/browser/callhelpers-pinvoke.cpp:22

  • CompressionNative_CompressBound was removed from the generated WASM callhelpers P/Invoke table, but managed code still declares a [LibraryImport] for it (Interop.ZLib.compressBound). If this entry is missing at runtime, CompressionNative_CompressBound resolution is likely to fail (EntryPointNotFound / missing symbol) on browser/WASI.

Please ensure the callhelpers generator includes CompressionNative_CompressBound in both browser and WASI outputs (and the table counts match).

  • Files reviewed: 155/155 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Keep the CompressionNative_CompressBound entry in the browser and WASI static P/Invoke tables and update their entry counts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 21:58

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The marshaller’s special-status handling can preserve stale OutOfMemoryException stack traces due to reuse of the preallocated instance, which should be corrected before merging.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 155/155 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment on lines +41 to +61
Exception exception;
if ((nuint)status <= 2)
{
exception = Thread.GetQCallSpecialException(status);
}
else
{
GCHandle exceptionHandle = GCHandle.FromIntPtr(status);
try
{
exception = (Exception)exceptionHandle.Target!;
}
finally
{
exceptionHandle.Free();
}
}

// Throw during unmarshalling so QCall exception propagation remains as close as
// possible to throwing directly from native code, as QCalls did previously.
ExceptionDispatchInfo.Throw(exception);
Comment on lines +60 to +65
internal static Exception GetQCallSpecialException(nint status)
{
Exception? exception = null;
GetQCallSpecialException(status, ObjectHandleOnStack.Create(ref exception));
return exception!;
}
// [DllImport(JitHelpers.QCall, EntryPoint = "FooNative_Bar", CharSet = CharSet.Unicode)]
// // QCalls should always be static extern.
// private static extern bool Bar(int flags, string inString, StringHandleOnStack retString);
// // All QCalls that use BEGIN_QCALL must use the hidden last parameter error handler.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also described in docs\design\coreclr\botr\corelib.md . Can we update that copy as well? (Or deduplicate the description by replacing one of the copies with a link.)

{
"System.StubHelpers.StubHelpers.ThrowInteropParamException",
"Server.Contract.IStringTesting.Reverse_LPWStr_OutAttr",
"Xunit.Assert.RecordException",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is likely to be unreliable with JIT stress when some of the methods can be aggressively inlined and no longer show up in the stacktrace.

@jkotas

Copy link
Copy Markdown
Member

LGTM otherwise

internal static unsafe partial class ThrowHelpers
{
[DoesNotReturn]
[ErrorHandler(typeof(QCallExceptionStatusMarshaller), ErrorLocation.HiddenLastParameter)]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Reposting earlier question since it got lost in the comment noise)

Was there a specific problem with HiddenReturnValue that made you switch to HiddenLastParameter?

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@davidwrighton@jkotas@AustinWise
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Route QCall exceptions through marshalling - #132420

Open
davidwrighton wants to merge 29 commits into
dotnet:mainfrom
davidwrighton:fixup_qcalls
Open

Route QCall exceptions through marshalling#132420
davidwrighton wants to merge 29 commits into
dotnet:mainfrom
davidwrighton:fixup_qcalls

Conversation

@davidwrighton

@davidwrightondavidwrighton commented Aug 17, 2026

Copy link
Copy Markdown
Member

Summary

  • add source-generated LibraryImport support for custom error handlers sourced from the native return value, final parameter, or injected hidden return/parameter locations
  • use the hidden-return error-handler form for CoreCLR QCalls that use BEGIN_QCALL / END_QCALL, while preserving their existing managed signatures
  • change migrated native QCall entry points to return QCallExceptionStatus and move ordinary native return values to explicit out parameters
  • capture exceptions at the QCall boundary and return either a reserved status for preallocated OutOfMemoryException / StackOverflowException instances or a handle to the exact managed exception
  • convert and rethrow the status during managed unmarshalling, preserving the original exception instance and stack trace and releasing exception handles after transfer
  • keep QCalls without the exception-handling macros on their existing ABI, and retain binder-visible helper signatures through managed wrappers where necessary
  • update native declarations and generated browser/WASI call helpers for the new QCall ABI

Testing

  • build.cmd clr+libs -c Checked
  • build.cmd mono+libs
  • build.cmd clr+libs -os browser -c Debug
  • build.cmd clr.corelib -os osx -a x64 -c Debug
  • build.cmd clr.runtime -a x86 -c Checked
  • src\tasks\WasmAppBuilder\generate-coreclr-helpers.cmd -c Debug
  • AmbiguousImplementationException runtime test, including repeated exception propagation
  • RuntimeHelpersTests.QCallExceptionStatus_ThrowsSpecialException
  • RuntimeHelpersTests.QCallExceptionStatus_ThrowsExactExceptionFromHandle
  • COM string marshalling tests covering preservation of the customer-visible exception stack trace
  • migration audit confirming only QCalls using BEGIN_QCALL were converted
  • git diff --check

Note

This pull request description was generated by GitHub Copilot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR changes CoreCLR QCall exception propagation by routing exceptions thrown inside BEGIN_QCALL/END_QCALL through an out QCallException status value that is custom-marshalled back to managed code. The native side captures throwables onto the managed Thread, and the managed marshaller rethrows during unmarshalling, while leaving non-BEGIN_QCALL QCalls on the prior behavior.

Changes:

  • Introduce QCallException + QCallExceptionMarshaller and update managed QCall signatures to include an out QCallException for BEGIN_QCALL-based QCalls.
  • Update native QCall entrypoints and exception macros to capture exceptions instead of dispatching/rethrowing directly across the boundary.
  • Add the per-thread storage needed to temporarily root the captured throwable and rethrow it from managed unmarshalling.

Reviewed changes

Copilot reviewed 170 out of 170 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiFinalizerScheduler.csAdd conditional out QCallException to WASI finalizer QCalls.
src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.csPass conditional out _ to updated reentrant wait QCall.
src/libraries/System.Private.CoreLib/src/System/Threading/Thread.csUpdate CurrentOSThreadId QCall invocation to include conditional out _.
src/libraries/System.Private.CoreLib/src/System/Threading/Lock.csUse updated Environment.GetProcessorCount signature under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.csThread out QCallException through multiple tracing/ALC QCalls.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.csAdd out QCallException to QCalls used by type map cache population/lookups.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCMarshal.csPass out _ to updated ObjC marshal QCalls.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NoCom.csExtend internal signature with conditional out _ under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.csCall updated RegisterIsRootedCallback under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.csUpdate tracing QCall invocations to include out _.
src/libraries/System.Private.CoreLib/src/System/Environment.csUpdate ProcessorCount initialization to use CORECLR QCall signature.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/XplatEventLogger.csAdd out QCallException plumbing to xplat EventSource QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventProvider.csAdd conditional out _ to EventPipeInternal QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.Threads.csAdd conditional out _ to EventPipe session wait/disable QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.csAdd conditional out _ to EventPipe dispatcher QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipe.csAdd conditional out _ to EventPipe enable QCall.
src/libraries/System.Private.CoreLib/src/System/AppContext.csAdd conditional out _ to host property QCall path.
src/coreclr/vm/weakreferencenative.hAdd QCallException* to COM weak ref QCall signatures.
src/coreclr/vm/weakreferencenative.cppAdd QCallException* parameter to COM weak ref QCall implementations.
src/coreclr/vm/wasm/entrypoints.hAdd QCallException* to WASI finalizer exported entrypoints.
src/coreclr/vm/versionresilienthashcode.hAdd QCallException* to version-resilient hash QCall signature.
src/coreclr/vm/versionresilienthashcode.cppAdd QCallException* to version-resilient hash QCall implementation.
src/coreclr/vm/varargsnative.hAdd QCallException* to varargs iterator QCall signatures.
src/coreclr/vm/varargsnative.cppAdd QCallException* to varargs iterator QCall implementations.
src/coreclr/vm/unsafeaccessors.cppAdd QCallException* to unsafe accessor resolver QCall.
src/coreclr/vm/tailcallhelp.hAdd QCallException* to tailcall arg buffer allocation QCall.
src/coreclr/vm/tailcallhelp.cppAdd QCallException* to tailcall arg buffer allocation QCall implementation.
src/coreclr/vm/stubhelpers.hAdd QCallException* to multiple interop/stub helper QCall signatures.
src/coreclr/vm/stackwalk.hAdd QCallException* to AppendExceptionStackFrame QCall signature.
src/coreclr/vm/reflectioninvocation.hAdd QCallException* to reflection invocation QCall signatures.
src/coreclr/vm/qcall.hUpdate QCall guidance/comments and route BEGIN/END macros to capture dispatcher.
src/coreclr/vm/qcall.cppAdd QCallException::SetThrowable implementation and thread rooting logic.
src/coreclr/vm/olevariant.hAdd QCallException* to VARIANT record conversion QCall signature.
src/coreclr/vm/olevariant.cppAdd QCallException* to VARIANT record conversion QCall implementation.
src/coreclr/vm/object.hAdd m_QCallException field + setter on ThreadBaseObject.
src/coreclr/vm/nativelibrarynative.hAdd QCallException* to NativeLibrary load-by-name QCall signature.
src/coreclr/vm/nativelibrarynative.cppAdd QCallException* to NativeLibrary load-by-name QCall implementation.
src/coreclr/vm/nativeeventsource.hAdd QCallException* to EventSource/perf tracing QCall signatures.
src/coreclr/vm/multicorejit.hAdd QCallException* to MultiCoreJIT QCall signatures.
src/coreclr/vm/multicorejit.cppAdd QCallException* to MultiCoreJIT QCall implementations.
src/coreclr/vm/mlinfo.cppAdd QCallException* to StubHelpers_CreateCustomMarshaler QCall implementation.
src/coreclr/vm/method.hppAdd QCallException* to unsafe accessor resolver declaration.
src/coreclr/vm/managedmdimport.hppAdd QCallException* to metadata enum QCall signature.
src/coreclr/vm/managedmdimport.cppAdd QCallException* to metadata enum QCall implementation.
src/coreclr/vm/loaderallocator.hppAdd QCallException* to loader allocator destroy QCall signature.
src/coreclr/vm/loaderallocator.cppAdd QCallException* to loader allocator destroy QCall implementation.
src/coreclr/vm/JitQCallHelpers.hAdd QCallException* to JIT helper QCall signatures.
src/coreclr/vm/jithelpers.cppAdd QCallException* to JIT helper QCalls and adjust handler installation in rare paths.
src/coreclr/vm/interoplibinterface.hAdd QCallException* to ObjC/Java marshal QCall signatures.
src/coreclr/vm/interoplibinterface_objc.cppAdd QCallException* to ObjC marshal QCall implementations.
src/coreclr/vm/interoplibinterface_java.cppAdd QCallException* to Java marshal QCall implementations.
src/coreclr/vm/interoplibinterface_comwrappers.hAdd QCallException* to ComWrappers QCall signatures.
src/coreclr/vm/interoplibinterface_comwrappers.cppAdd QCallException* to ComWrappers QCall implementations.
src/coreclr/vm/ilmarshalers.hAdd QCallException* to safe array marshaler QCall signatures.
src/coreclr/vm/ilmarshalers.cppAdd QCallException* to safe array marshaler QCall implementations.
src/coreclr/vm/finalizerthread.cppAdd QCallException* to WASI finalizer QCall implementations.
src/coreclr/vm/exceptmacros.hIntroduce QCallException native struct and capture-dispatcher exception macros.
src/coreclr/vm/exceptionhandlingqcalls.hAdd QCallException* to exception-handling-related QCall signatures.
src/coreclr/vm/exceptionhandling.cppAdd QCallException* to exception-handling-related QCall implementations.
src/coreclr/vm/excep.cppAdd QCallException* to message QCalls and introduce QCall-specific unwind helper.
src/coreclr/vm/eventpipeinternal.hAdd QCallException* to EventPipeInternal QCall signatures.
src/coreclr/vm/eventpipeinternal.cppAdd QCallException* to EventPipeInternal QCall implementations.
src/coreclr/vm/debugdebugger.hAdd QCallException* to debugger/stacktrace QCall signatures.
src/coreclr/vm/debugdebugger.cppAdd QCallException* to debugger/stacktrace QCall implementations.
src/coreclr/vm/customattribute.hAdd QCallException* to custom attribute QCall signatures.
src/coreclr/vm/customattribute.cppAdd QCallException* to custom attribute QCall implementations.
src/coreclr/vm/corelib.hAdd corelib field mapping for new thread _qcallException field.
src/coreclr/vm/coreassemblyspec.cppAdd QCallException* to assembly spec initialization QCall signature.
src/coreclr/vm/comsynchronizable.hAdd QCallException* to thread/lock-related QCall signatures.
src/coreclr/vm/comsynchronizable.cppAdd QCallException* to thread/lock-related QCall implementations.
src/coreclr/vm/commodule.hAdd QCallException* to Reflection.Emit module builder QCall signatures.
src/coreclr/vm/commodule.cppAdd QCallException* to Reflection.Emit module builder QCall implementations.
src/coreclr/vm/comdependenthandle.hAdd QCallException* to dependent handle QCall signatures.
src/coreclr/vm/comdependenthandle.cppAdd QCallException* to dependent handle QCall implementations.
src/coreclr/vm/comdelegate.hAdd QCallException* to delegate-related QCall signatures.
src/coreclr/vm/comdelegate.cppAdd QCallException* to delegate-related QCall implementations.
src/coreclr/vm/clrconfignative.hAdd QCallException* to CLR config QCall signature.
src/coreclr/vm/clrconfignative.cppAdd QCallException* to CLR config QCall implementation.
src/coreclr/vm/arraynative.hAdd QCallException* to array creation/ctor QCall signatures.
src/coreclr/vm/arraynative.cppAdd QCallException* to array creation/ctor QCall implementations.
src/coreclr/vm/appdomainnative.hppAdd QCallException* to AppDomain/AppContext/string intern QCall signatures.
src/coreclr/vm/appdomainnative.cppAdd QCallException* to AppDomain/AppContext/string intern QCall implementations.
src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csprojInclude new QCallException.cs in CoreCLR CoreLib build.
src/coreclr/System.Private.CoreLib/src/System/Variant.csAdd out QCallException to COM VARIANT-related QCalls.
src/coreclr/System.Private.CoreLib/src/System/ValueType.csAdd out QCallException to ValueType helper QCalls.
src/coreclr/System.Private.CoreLib/src/System/TypeLoadException.CoreCLR.csAdd out QCallException to message-formatting QCall.
src/coreclr/System.Private.CoreLib/src/System/Threading/ObjectHeader.CoreCLR.csAdd out QCallException to lock-object QCall.
src/coreclr/System.Private.CoreLib/src/System/String.CoreCLR.csAdd out QCallException to string intern/is-interned QCalls.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CreateUninitializedCache.CoreCLR.csAdd out QCallException to create-uninitialized info QCall.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.csAdd out QCallException to multiple RuntimeType/reflection QCalls.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.BoxCache.csAdd out QCallException to boxing cache QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/TrackerObjectManager.CoreCLR.csAdd out QCallException to wrapper-cache registration QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.CoreCLR.csAdd out QCallException to ObjC marshal QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.CoreCLR.csAdd out QCallException to NativeLibrary load QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/JavaMarshal.CoreCLR.csAdd out QCallException to Java marshal QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.csAdd out QCallException to GCHandle QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.CoreCLR.csAdd out QCallException to ComWrappers QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.csAdd out QCallException to EH-related QCall internal calls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/DependentHandle.csAdd out QCallException to dependent handle QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/ControlledExecution.CoreCLR.csAdd out QCallException to ThreadNative_Abort QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/VirtualDispatchHelpers.csAdd out QCallException to virtual dispatch resolver QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/StaticsHelpers.csAdd out QCallException to thread statics QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallException.csIntroduce managed QCall exception status type + custom marshaller.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/InitHelpers.csAdd out QCallException to init-class QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/GenericsHelpers.csAdd out QCallException to generic handle worker QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.csAdd out QCallException to cast helper QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.csAdd out QCallException to async continuation QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.CoreCLR.csAdd out QCallException to unsafe accessor resolver QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.csAdd out QCallException to module name/types QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.csAdd out QCallException to custom attribute QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.csAdd out QCallException to GetCurrentMethod QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.csAdd out QCallException to metadata update QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.csAdd out QCallException to raw metadata QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.csAdd out QCallException to metadata import enum QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/LoaderAllocator.csAdd out QCallException to loader allocator destroy QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeParameterBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.csAdd out QCallException to dynamic assembly creation QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.CoreCLR.csAdd out _ to compile-method QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.csAdd out _ to destroy handle QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/Reflection/AssemblyName.CoreCLR.csAdd out QCallException to assembly spec init QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.csAdd out QCallException to assembly lookup QCalls.
src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.csAdd out _ to clone allocation QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/IO/Stream.CoreCLR.csAdd out QCallException to overridden-check QCall.
src/coreclr/System.Private.CoreLib/src/System/IO/FileLoadException.CoreCLR.csAdd out QCallException to file load message QCalls.
src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.csAdd out QCallException to exception-related QCalls.
src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.csAdd out QCallException to Environment QCalls (Exit/FailFast/GetProcessorCount).
src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.csAdd out QCallException to enum values/names QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreCLR.csAdd out QCallException to stack trace frames QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrame.CoreCLR.csAdd out QCallException to method-desc-from-IP QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Debugger.csAdd out QCallException to debugger break/notification QCalls.
src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.csAdd out QCallException to delegate binding/construct QCalls.
src/coreclr/System.Private.CoreLib/src/System/ComAwareWeakReference.CoreCLR.csAdd out QCallException to COM weak ref QCalls.
src/coreclr/System.Private.CoreLib/src/System/CLRConfig.csAdd out QCallException to CLR config QCall.
src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.csAdd out QCallException to array creation/ctor QCalls.
src/coreclr/System.Private.CoreLib/src/System/ArgIterator.csAdd out QCallException to varargs iterator QCalls.
src/coreclr/System.Private.CoreLib/src/System/AppContext.CoreCLR.csAdd out QCallException to host property QCall.
src/coreclr/System.Private.CoreLib/src/Internal/VersionResilientHashCode.CoreCLR.csAdd out QCallException to version-resilient hash QCall.
src/coreclr/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.csAdd out QCallException to throw-helper QCalls.
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.csAdd conditional out _ to EH QCall internal calls.
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.csAdd conditional out _ to EH QCall internal calls.

Comment threadsrc/coreclr/vm/exceptmacros.h
@azure-pipelines

Copy link
Copy Markdown
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.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
CopilotAI review requested due to automatic review settings August 18, 2026 00:15

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 170 out of 170 changed files in this pull request and generated 1 comment.

CopilotAI review requested due to automatic review settings August 18, 2026 19:45

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 171 out of 171 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallException.cs:26

  • QCallExceptionMarshaller.FromUnmanaged only treats exceptionPending == 1 as an exception. Since the native representation is an INT32 flag, using a non-zero check (!= 0) is more robust against future expansion or accidental non-1 values, and avoids silently skipping exception propagation if the flag is ever set to another non-zero value.

Add managed forwarding overloads for helpers invoked by native IL stub generation so their CoreLib binder signatures remain stable after adding QCall exception parameters.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
Clean up accidental formatting and redundant conditionals introduced by the QCall migration. Make the COM stack trace assertion tolerate non-frame and additional lines while preserving frame-order validation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 31769fe8-86a0-4379-91f3-5353097eb47c
CopilotAI review requested due to automatic review settings September 1, 2026 19:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs:794

  • GetMethod(...) can return null, which would make failures here surface as a NullReferenceException later (or even pass silently if callers change). Since this helper is only for tests, it’s better to assert the MethodInfo is found so failures are clear and localized.
    src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallExceptionStatus.cs:45
  • The <= 2 check is a protocol constant that’s easy to lose track of when the native side evolves. Consider naming it as a constant so the intent is explicit and future updates are harder to miss.
  • Files reviewed: 155/155 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment threadsrc/tests/Interop/COM/NETClients/Primitives/StringTests.cs
Restore normal native QCall return values and pass exception status through a hidden final parameter.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 02:19

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It changes CoreCLR’s QCall exception-handling ABI and dispatch machinery across a wide set of managed and native entrypoints, making the regression surface too large to approve without focused human validation.

Review details
  • Files reviewed: 155/155 changed files
  • Comments generated: 1
  • Review effort level: Lite

@jkotas

jkotas commented Sep 2, 2026

Copy link
Copy Markdown
Member

Restore normal native QCall return values and pass exception status through a hidden final parameter.

Was there a specific problem with HiddenReturnValue that made you switch to HiddenLastParameter?

Comment threadsrc/coreclr/vm/excep.cpp Outdated
Comment threadsrc/coreclr/vm/exceptionhandling.cpp Outdated
Avoid relying on GET_THREAD and explicit MAKE_CURRENT_THREAD_AVAILABLE calls in QCall exception and reflection paths. Retrieve the current thread directly with GetThread, and correct the QCall casing in the exception rethrow helper name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 21:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The generated WASM callhelpers tables appear to drop the CompressionNative_CompressBound entry while managed code still imports it, which risks runtime entrypoint resolution failures on browser/WASI.

Review details

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallExceptionStatus.cs:45

  • QCallExceptionStatusMarshaller treats status values 1 and 2 as special cases, but the code currently uses a hard-coded magic number ("<= 2"). This makes the reserved-status contract harder to maintain and audit.

Consider introducing named constants (e.g., QCallOutOfMemoryExceptionStatus = 1, QCallStackOverflowExceptionStatus = 2, and/or MaxSpecialExceptionStatus = 2) and using them in the comparison.

src/coreclr/vm/wasm/browser/callhelpers-pinvoke.cpp:22

  • CompressionNative_CompressBound was removed from the generated WASM callhelpers P/Invoke table, but managed code still declares a [LibraryImport] for it (Interop.ZLib.compressBound). If this entry is missing at runtime, CompressionNative_CompressBound resolution is likely to fail (EntryPointNotFound / missing symbol) on browser/WASI.

Please ensure the callhelpers generator includes CompressionNative_CompressBound in both browser and WASI outputs (and the table counts match).

  • Files reviewed: 155/155 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Keep the CompressionNative_CompressBound entry in the browser and WASI static P/Invoke tables and update their entry counts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 21:58

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The marshaller’s special-status handling can preserve stale OutOfMemoryException stack traces due to reuse of the preallocated instance, which should be corrected before merging.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 155/155 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment on lines +41 to +61
Exception exception;
if ((nuint)status <= 2)
{
exception = Thread.GetQCallSpecialException(status);
}
else
{
GCHandle exceptionHandle = GCHandle.FromIntPtr(status);
try
{
exception = (Exception)exceptionHandle.Target!;
}
finally
{
exceptionHandle.Free();
}
}

// Throw during unmarshalling so QCall exception propagation remains as close as
// possible to throwing directly from native code, as QCalls did previously.
ExceptionDispatchInfo.Throw(exception);
Comment on lines +60 to +65
internal static Exception GetQCallSpecialException(nint status)
{
Exception? exception = null;
GetQCallSpecialException(status, ObjectHandleOnStack.Create(ref exception));
return exception!;
}
// [DllImport(JitHelpers.QCall, EntryPoint = "FooNative_Bar", CharSet = CharSet.Unicode)]
// // QCalls should always be static extern.
// private static extern bool Bar(int flags, string inString, StringHandleOnStack retString);
// // All QCalls that use BEGIN_QCALL must use the hidden last parameter error handler.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also described in docs\design\coreclr\botr\corelib.md . Can we update that copy as well? (Or deduplicate the description by replacing one of the copies with a link.)

{
"System.StubHelpers.StubHelpers.ThrowInteropParamException",
"Server.Contract.IStringTesting.Reverse_LPWStr_OutAttr",
"Xunit.Assert.RecordException",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is likely to be unreliable with JIT stress when some of the methods can be aggressively inlined and no longer show up in the stacktrace.

@jkotas

Copy link
Copy Markdown
Member

LGTM otherwise

internal static unsafe partial class ThrowHelpers
{
[DoesNotReturn]
[ErrorHandler(typeof(QCallExceptionStatusMarshaller), ErrorLocation.HiddenLastParameter)]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Reposting earlier question since it got lost in the comment noise)

Was there a specific problem with HiddenReturnValue that made you switch to HiddenLastParameter?

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@davidwrighton@jkotas@AustinWise
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Route QCall exceptions through marshalling - #132420

Open
davidwrighton wants to merge 29 commits into
dotnet:mainfrom
davidwrighton:fixup_qcalls
Open

Route QCall exceptions through marshalling#132420
davidwrighton wants to merge 29 commits into
dotnet:mainfrom
davidwrighton:fixup_qcalls

Conversation

@davidwrighton

@davidwrightondavidwrighton commented Aug 17, 2026

Copy link
Copy Markdown
Member

Summary

  • add source-generated LibraryImport support for custom error handlers sourced from the native return value, final parameter, or injected hidden return/parameter locations
  • use the hidden-return error-handler form for CoreCLR QCalls that use BEGIN_QCALL / END_QCALL, while preserving their existing managed signatures
  • change migrated native QCall entry points to return QCallExceptionStatus and move ordinary native return values to explicit out parameters
  • capture exceptions at the QCall boundary and return either a reserved status for preallocated OutOfMemoryException / StackOverflowException instances or a handle to the exact managed exception
  • convert and rethrow the status during managed unmarshalling, preserving the original exception instance and stack trace and releasing exception handles after transfer
  • keep QCalls without the exception-handling macros on their existing ABI, and retain binder-visible helper signatures through managed wrappers where necessary
  • update native declarations and generated browser/WASI call helpers for the new QCall ABI

Testing

  • build.cmd clr+libs -c Checked
  • build.cmd mono+libs
  • build.cmd clr+libs -os browser -c Debug
  • build.cmd clr.corelib -os osx -a x64 -c Debug
  • build.cmd clr.runtime -a x86 -c Checked
  • src\tasks\WasmAppBuilder\generate-coreclr-helpers.cmd -c Debug
  • AmbiguousImplementationException runtime test, including repeated exception propagation
  • RuntimeHelpersTests.QCallExceptionStatus_ThrowsSpecialException
  • RuntimeHelpersTests.QCallExceptionStatus_ThrowsExactExceptionFromHandle
  • COM string marshalling tests covering preservation of the customer-visible exception stack trace
  • migration audit confirming only QCalls using BEGIN_QCALL were converted
  • git diff --check

Note

This pull request description was generated by GitHub Copilot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR changes CoreCLR QCall exception propagation by routing exceptions thrown inside BEGIN_QCALL/END_QCALL through an out QCallException status value that is custom-marshalled back to managed code. The native side captures throwables onto the managed Thread, and the managed marshaller rethrows during unmarshalling, while leaving non-BEGIN_QCALL QCalls on the prior behavior.

Changes:

  • Introduce QCallException + QCallExceptionMarshaller and update managed QCall signatures to include an out QCallException for BEGIN_QCALL-based QCalls.
  • Update native QCall entrypoints and exception macros to capture exceptions instead of dispatching/rethrowing directly across the boundary.
  • Add the per-thread storage needed to temporarily root the captured throwable and rethrow it from managed unmarshalling.

Reviewed changes

Copilot reviewed 170 out of 170 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiFinalizerScheduler.csAdd conditional out QCallException to WASI finalizer QCalls.
src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.csPass conditional out _ to updated reentrant wait QCall.
src/libraries/System.Private.CoreLib/src/System/Threading/Thread.csUpdate CurrentOSThreadId QCall invocation to include conditional out _.
src/libraries/System.Private.CoreLib/src/System/Threading/Lock.csUse updated Environment.GetProcessorCount signature under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.csThread out QCallException through multiple tracing/ALC QCalls.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.csAdd out QCallException to QCalls used by type map cache population/lookups.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCMarshal.csPass out _ to updated ObjC marshal QCalls.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NoCom.csExtend internal signature with conditional out _ under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.csCall updated RegisterIsRootedCallback under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.csUpdate tracing QCall invocations to include out _.
src/libraries/System.Private.CoreLib/src/System/Environment.csUpdate ProcessorCount initialization to use CORECLR QCall signature.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/XplatEventLogger.csAdd out QCallException plumbing to xplat EventSource QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventProvider.csAdd conditional out _ to EventPipeInternal QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.Threads.csAdd conditional out _ to EventPipe session wait/disable QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.csAdd conditional out _ to EventPipe dispatcher QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipe.csAdd conditional out _ to EventPipe enable QCall.
src/libraries/System.Private.CoreLib/src/System/AppContext.csAdd conditional out _ to host property QCall path.
src/coreclr/vm/weakreferencenative.hAdd QCallException* to COM weak ref QCall signatures.
src/coreclr/vm/weakreferencenative.cppAdd QCallException* parameter to COM weak ref QCall implementations.
src/coreclr/vm/wasm/entrypoints.hAdd QCallException* to WASI finalizer exported entrypoints.
src/coreclr/vm/versionresilienthashcode.hAdd QCallException* to version-resilient hash QCall signature.
src/coreclr/vm/versionresilienthashcode.cppAdd QCallException* to version-resilient hash QCall implementation.
src/coreclr/vm/varargsnative.hAdd QCallException* to varargs iterator QCall signatures.
src/coreclr/vm/varargsnative.cppAdd QCallException* to varargs iterator QCall implementations.
src/coreclr/vm/unsafeaccessors.cppAdd QCallException* to unsafe accessor resolver QCall.
src/coreclr/vm/tailcallhelp.hAdd QCallException* to tailcall arg buffer allocation QCall.
src/coreclr/vm/tailcallhelp.cppAdd QCallException* to tailcall arg buffer allocation QCall implementation.
src/coreclr/vm/stubhelpers.hAdd QCallException* to multiple interop/stub helper QCall signatures.
src/coreclr/vm/stackwalk.hAdd QCallException* to AppendExceptionStackFrame QCall signature.
src/coreclr/vm/reflectioninvocation.hAdd QCallException* to reflection invocation QCall signatures.
src/coreclr/vm/qcall.hUpdate QCall guidance/comments and route BEGIN/END macros to capture dispatcher.
src/coreclr/vm/qcall.cppAdd QCallException::SetThrowable implementation and thread rooting logic.
src/coreclr/vm/olevariant.hAdd QCallException* to VARIANT record conversion QCall signature.
src/coreclr/vm/olevariant.cppAdd QCallException* to VARIANT record conversion QCall implementation.
src/coreclr/vm/object.hAdd m_QCallException field + setter on ThreadBaseObject.
src/coreclr/vm/nativelibrarynative.hAdd QCallException* to NativeLibrary load-by-name QCall signature.
src/coreclr/vm/nativelibrarynative.cppAdd QCallException* to NativeLibrary load-by-name QCall implementation.
src/coreclr/vm/nativeeventsource.hAdd QCallException* to EventSource/perf tracing QCall signatures.
src/coreclr/vm/multicorejit.hAdd QCallException* to MultiCoreJIT QCall signatures.
src/coreclr/vm/multicorejit.cppAdd QCallException* to MultiCoreJIT QCall implementations.
src/coreclr/vm/mlinfo.cppAdd QCallException* to StubHelpers_CreateCustomMarshaler QCall implementation.
src/coreclr/vm/method.hppAdd QCallException* to unsafe accessor resolver declaration.
src/coreclr/vm/managedmdimport.hppAdd QCallException* to metadata enum QCall signature.
src/coreclr/vm/managedmdimport.cppAdd QCallException* to metadata enum QCall implementation.
src/coreclr/vm/loaderallocator.hppAdd QCallException* to loader allocator destroy QCall signature.
src/coreclr/vm/loaderallocator.cppAdd QCallException* to loader allocator destroy QCall implementation.
src/coreclr/vm/JitQCallHelpers.hAdd QCallException* to JIT helper QCall signatures.
src/coreclr/vm/jithelpers.cppAdd QCallException* to JIT helper QCalls and adjust handler installation in rare paths.
src/coreclr/vm/interoplibinterface.hAdd QCallException* to ObjC/Java marshal QCall signatures.
src/coreclr/vm/interoplibinterface_objc.cppAdd QCallException* to ObjC marshal QCall implementations.
src/coreclr/vm/interoplibinterface_java.cppAdd QCallException* to Java marshal QCall implementations.
src/coreclr/vm/interoplibinterface_comwrappers.hAdd QCallException* to ComWrappers QCall signatures.
src/coreclr/vm/interoplibinterface_comwrappers.cppAdd QCallException* to ComWrappers QCall implementations.
src/coreclr/vm/ilmarshalers.hAdd QCallException* to safe array marshaler QCall signatures.
src/coreclr/vm/ilmarshalers.cppAdd QCallException* to safe array marshaler QCall implementations.
src/coreclr/vm/finalizerthread.cppAdd QCallException* to WASI finalizer QCall implementations.
src/coreclr/vm/exceptmacros.hIntroduce QCallException native struct and capture-dispatcher exception macros.
src/coreclr/vm/exceptionhandlingqcalls.hAdd QCallException* to exception-handling-related QCall signatures.
src/coreclr/vm/exceptionhandling.cppAdd QCallException* to exception-handling-related QCall implementations.
src/coreclr/vm/excep.cppAdd QCallException* to message QCalls and introduce QCall-specific unwind helper.
src/coreclr/vm/eventpipeinternal.hAdd QCallException* to EventPipeInternal QCall signatures.
src/coreclr/vm/eventpipeinternal.cppAdd QCallException* to EventPipeInternal QCall implementations.
src/coreclr/vm/debugdebugger.hAdd QCallException* to debugger/stacktrace QCall signatures.
src/coreclr/vm/debugdebugger.cppAdd QCallException* to debugger/stacktrace QCall implementations.
src/coreclr/vm/customattribute.hAdd QCallException* to custom attribute QCall signatures.
src/coreclr/vm/customattribute.cppAdd QCallException* to custom attribute QCall implementations.
src/coreclr/vm/corelib.hAdd corelib field mapping for new thread _qcallException field.
src/coreclr/vm/coreassemblyspec.cppAdd QCallException* to assembly spec initialization QCall signature.
src/coreclr/vm/comsynchronizable.hAdd QCallException* to thread/lock-related QCall signatures.
src/coreclr/vm/comsynchronizable.cppAdd QCallException* to thread/lock-related QCall implementations.
src/coreclr/vm/commodule.hAdd QCallException* to Reflection.Emit module builder QCall signatures.
src/coreclr/vm/commodule.cppAdd QCallException* to Reflection.Emit module builder QCall implementations.
src/coreclr/vm/comdependenthandle.hAdd QCallException* to dependent handle QCall signatures.
src/coreclr/vm/comdependenthandle.cppAdd QCallException* to dependent handle QCall implementations.
src/coreclr/vm/comdelegate.hAdd QCallException* to delegate-related QCall signatures.
src/coreclr/vm/comdelegate.cppAdd QCallException* to delegate-related QCall implementations.
src/coreclr/vm/clrconfignative.hAdd QCallException* to CLR config QCall signature.
src/coreclr/vm/clrconfignative.cppAdd QCallException* to CLR config QCall implementation.
src/coreclr/vm/arraynative.hAdd QCallException* to array creation/ctor QCall signatures.
src/coreclr/vm/arraynative.cppAdd QCallException* to array creation/ctor QCall implementations.
src/coreclr/vm/appdomainnative.hppAdd QCallException* to AppDomain/AppContext/string intern QCall signatures.
src/coreclr/vm/appdomainnative.cppAdd QCallException* to AppDomain/AppContext/string intern QCall implementations.
src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csprojInclude new QCallException.cs in CoreCLR CoreLib build.
src/coreclr/System.Private.CoreLib/src/System/Variant.csAdd out QCallException to COM VARIANT-related QCalls.
src/coreclr/System.Private.CoreLib/src/System/ValueType.csAdd out QCallException to ValueType helper QCalls.
src/coreclr/System.Private.CoreLib/src/System/TypeLoadException.CoreCLR.csAdd out QCallException to message-formatting QCall.
src/coreclr/System.Private.CoreLib/src/System/Threading/ObjectHeader.CoreCLR.csAdd out QCallException to lock-object QCall.
src/coreclr/System.Private.CoreLib/src/System/String.CoreCLR.csAdd out QCallException to string intern/is-interned QCalls.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CreateUninitializedCache.CoreCLR.csAdd out QCallException to create-uninitialized info QCall.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.csAdd out QCallException to multiple RuntimeType/reflection QCalls.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.BoxCache.csAdd out QCallException to boxing cache QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/TrackerObjectManager.CoreCLR.csAdd out QCallException to wrapper-cache registration QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.CoreCLR.csAdd out QCallException to ObjC marshal QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.CoreCLR.csAdd out QCallException to NativeLibrary load QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/JavaMarshal.CoreCLR.csAdd out QCallException to Java marshal QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.csAdd out QCallException to GCHandle QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.CoreCLR.csAdd out QCallException to ComWrappers QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.csAdd out QCallException to EH-related QCall internal calls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/DependentHandle.csAdd out QCallException to dependent handle QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/ControlledExecution.CoreCLR.csAdd out QCallException to ThreadNative_Abort QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/VirtualDispatchHelpers.csAdd out QCallException to virtual dispatch resolver QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/StaticsHelpers.csAdd out QCallException to thread statics QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallException.csIntroduce managed QCall exception status type + custom marshaller.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/InitHelpers.csAdd out QCallException to init-class QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/GenericsHelpers.csAdd out QCallException to generic handle worker QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.csAdd out QCallException to cast helper QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.csAdd out QCallException to async continuation QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.CoreCLR.csAdd out QCallException to unsafe accessor resolver QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.csAdd out QCallException to module name/types QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.csAdd out QCallException to custom attribute QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.csAdd out QCallException to GetCurrentMethod QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.csAdd out QCallException to metadata update QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.csAdd out QCallException to raw metadata QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.csAdd out QCallException to metadata import enum QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/LoaderAllocator.csAdd out QCallException to loader allocator destroy QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeParameterBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.csAdd out QCallException to dynamic assembly creation QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.CoreCLR.csAdd out _ to compile-method QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.csAdd out _ to destroy handle QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/Reflection/AssemblyName.CoreCLR.csAdd out QCallException to assembly spec init QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.csAdd out QCallException to assembly lookup QCalls.
src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.csAdd out _ to clone allocation QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/IO/Stream.CoreCLR.csAdd out QCallException to overridden-check QCall.
src/coreclr/System.Private.CoreLib/src/System/IO/FileLoadException.CoreCLR.csAdd out QCallException to file load message QCalls.
src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.csAdd out QCallException to exception-related QCalls.
src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.csAdd out QCallException to Environment QCalls (Exit/FailFast/GetProcessorCount).
src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.csAdd out QCallException to enum values/names QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreCLR.csAdd out QCallException to stack trace frames QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrame.CoreCLR.csAdd out QCallException to method-desc-from-IP QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Debugger.csAdd out QCallException to debugger break/notification QCalls.
src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.csAdd out QCallException to delegate binding/construct QCalls.
src/coreclr/System.Private.CoreLib/src/System/ComAwareWeakReference.CoreCLR.csAdd out QCallException to COM weak ref QCalls.
src/coreclr/System.Private.CoreLib/src/System/CLRConfig.csAdd out QCallException to CLR config QCall.
src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.csAdd out QCallException to array creation/ctor QCalls.
src/coreclr/System.Private.CoreLib/src/System/ArgIterator.csAdd out QCallException to varargs iterator QCalls.
src/coreclr/System.Private.CoreLib/src/System/AppContext.CoreCLR.csAdd out QCallException to host property QCall.
src/coreclr/System.Private.CoreLib/src/Internal/VersionResilientHashCode.CoreCLR.csAdd out QCallException to version-resilient hash QCall.
src/coreclr/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.csAdd out QCallException to throw-helper QCalls.
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.csAdd conditional out _ to EH QCall internal calls.
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.csAdd conditional out _ to EH QCall internal calls.

Comment threadsrc/coreclr/vm/exceptmacros.h
@azure-pipelines

Copy link
Copy Markdown
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.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
CopilotAI review requested due to automatic review settings August 18, 2026 00:15

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 170 out of 170 changed files in this pull request and generated 1 comment.

CopilotAI review requested due to automatic review settings August 18, 2026 19:45

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 171 out of 171 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallException.cs:26

  • QCallExceptionMarshaller.FromUnmanaged only treats exceptionPending == 1 as an exception. Since the native representation is an INT32 flag, using a non-zero check (!= 0) is more robust against future expansion or accidental non-1 values, and avoids silently skipping exception propagation if the flag is ever set to another non-zero value.

Add managed forwarding overloads for helpers invoked by native IL stub generation so their CoreLib binder signatures remain stable after adding QCall exception parameters.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
Clean up accidental formatting and redundant conditionals introduced by the QCall migration. Make the COM stack trace assertion tolerate non-frame and additional lines while preserving frame-order validation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 31769fe8-86a0-4379-91f3-5353097eb47c
CopilotAI review requested due to automatic review settings September 1, 2026 19:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs:794

  • GetMethod(...) can return null, which would make failures here surface as a NullReferenceException later (or even pass silently if callers change). Since this helper is only for tests, it’s better to assert the MethodInfo is found so failures are clear and localized.
    src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallExceptionStatus.cs:45
  • The <= 2 check is a protocol constant that’s easy to lose track of when the native side evolves. Consider naming it as a constant so the intent is explicit and future updates are harder to miss.
  • Files reviewed: 155/155 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment threadsrc/tests/Interop/COM/NETClients/Primitives/StringTests.cs
Restore normal native QCall return values and pass exception status through a hidden final parameter.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 02:19

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It changes CoreCLR’s QCall exception-handling ABI and dispatch machinery across a wide set of managed and native entrypoints, making the regression surface too large to approve without focused human validation.

Review details
  • Files reviewed: 155/155 changed files
  • Comments generated: 1
  • Review effort level: Lite

@jkotas

jkotas commented Sep 2, 2026

Copy link
Copy Markdown
Member

Restore normal native QCall return values and pass exception status through a hidden final parameter.

Was there a specific problem with HiddenReturnValue that made you switch to HiddenLastParameter?

Comment threadsrc/coreclr/vm/excep.cpp Outdated
Comment threadsrc/coreclr/vm/exceptionhandling.cpp Outdated
Avoid relying on GET_THREAD and explicit MAKE_CURRENT_THREAD_AVAILABLE calls in QCall exception and reflection paths. Retrieve the current thread directly with GetThread, and correct the QCall casing in the exception rethrow helper name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 21:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The generated WASM callhelpers tables appear to drop the CompressionNative_CompressBound entry while managed code still imports it, which risks runtime entrypoint resolution failures on browser/WASI.

Review details

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallExceptionStatus.cs:45

  • QCallExceptionStatusMarshaller treats status values 1 and 2 as special cases, but the code currently uses a hard-coded magic number ("<= 2"). This makes the reserved-status contract harder to maintain and audit.

Consider introducing named constants (e.g., QCallOutOfMemoryExceptionStatus = 1, QCallStackOverflowExceptionStatus = 2, and/or MaxSpecialExceptionStatus = 2) and using them in the comparison.

src/coreclr/vm/wasm/browser/callhelpers-pinvoke.cpp:22

  • CompressionNative_CompressBound was removed from the generated WASM callhelpers P/Invoke table, but managed code still declares a [LibraryImport] for it (Interop.ZLib.compressBound). If this entry is missing at runtime, CompressionNative_CompressBound resolution is likely to fail (EntryPointNotFound / missing symbol) on browser/WASI.

Please ensure the callhelpers generator includes CompressionNative_CompressBound in both browser and WASI outputs (and the table counts match).

  • Files reviewed: 155/155 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Keep the CompressionNative_CompressBound entry in the browser and WASI static P/Invoke tables and update their entry counts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 21:58

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The marshaller’s special-status handling can preserve stale OutOfMemoryException stack traces due to reuse of the preallocated instance, which should be corrected before merging.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 155/155 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment on lines +41 to +61
Exception exception;
if ((nuint)status <= 2)
{
exception = Thread.GetQCallSpecialException(status);
}
else
{
GCHandle exceptionHandle = GCHandle.FromIntPtr(status);
try
{
exception = (Exception)exceptionHandle.Target!;
}
finally
{
exceptionHandle.Free();
}
}

// Throw during unmarshalling so QCall exception propagation remains as close as
// possible to throwing directly from native code, as QCalls did previously.
ExceptionDispatchInfo.Throw(exception);
Comment on lines +60 to +65
internal static Exception GetQCallSpecialException(nint status)
{
Exception? exception = null;
GetQCallSpecialException(status, ObjectHandleOnStack.Create(ref exception));
return exception!;
}
// [DllImport(JitHelpers.QCall, EntryPoint = "FooNative_Bar", CharSet = CharSet.Unicode)]
// // QCalls should always be static extern.
// private static extern bool Bar(int flags, string inString, StringHandleOnStack retString);
// // All QCalls that use BEGIN_QCALL must use the hidden last parameter error handler.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also described in docs\design\coreclr\botr\corelib.md . Can we update that copy as well? (Or deduplicate the description by replacing one of the copies with a link.)

{
"System.StubHelpers.StubHelpers.ThrowInteropParamException",
"Server.Contract.IStringTesting.Reverse_LPWStr_OutAttr",
"Xunit.Assert.RecordException",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is likely to be unreliable with JIT stress when some of the methods can be aggressively inlined and no longer show up in the stacktrace.

@jkotas

Copy link
Copy Markdown
Member

LGTM otherwise

internal static unsafe partial class ThrowHelpers
{
[DoesNotReturn]
[ErrorHandler(typeof(QCallExceptionStatusMarshaller), ErrorLocation.HiddenLastParameter)]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Reposting earlier question since it got lost in the comment noise)

Was there a specific problem with HiddenReturnValue that made you switch to HiddenLastParameter?

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@davidwrighton@jkotas@AustinWise
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Route QCall exceptions through marshalling - #132420

Open
davidwrighton wants to merge 29 commits into
dotnet:mainfrom
davidwrighton:fixup_qcalls
Open

Route QCall exceptions through marshalling#132420
davidwrighton wants to merge 29 commits into
dotnet:mainfrom
davidwrighton:fixup_qcalls

Conversation

@davidwrighton

@davidwrightondavidwrighton commented Aug 17, 2026

Copy link
Copy Markdown
Member

Summary

  • add source-generated LibraryImport support for custom error handlers sourced from the native return value, final parameter, or injected hidden return/parameter locations
  • use the hidden-return error-handler form for CoreCLR QCalls that use BEGIN_QCALL / END_QCALL, while preserving their existing managed signatures
  • change migrated native QCall entry points to return QCallExceptionStatus and move ordinary native return values to explicit out parameters
  • capture exceptions at the QCall boundary and return either a reserved status for preallocated OutOfMemoryException / StackOverflowException instances or a handle to the exact managed exception
  • convert and rethrow the status during managed unmarshalling, preserving the original exception instance and stack trace and releasing exception handles after transfer
  • keep QCalls without the exception-handling macros on their existing ABI, and retain binder-visible helper signatures through managed wrappers where necessary
  • update native declarations and generated browser/WASI call helpers for the new QCall ABI

Testing

  • build.cmd clr+libs -c Checked
  • build.cmd mono+libs
  • build.cmd clr+libs -os browser -c Debug
  • build.cmd clr.corelib -os osx -a x64 -c Debug
  • build.cmd clr.runtime -a x86 -c Checked
  • src\tasks\WasmAppBuilder\generate-coreclr-helpers.cmd -c Debug
  • AmbiguousImplementationException runtime test, including repeated exception propagation
  • RuntimeHelpersTests.QCallExceptionStatus_ThrowsSpecialException
  • RuntimeHelpersTests.QCallExceptionStatus_ThrowsExactExceptionFromHandle
  • COM string marshalling tests covering preservation of the customer-visible exception stack trace
  • migration audit confirming only QCalls using BEGIN_QCALL were converted
  • git diff --check

Note

This pull request description was generated by GitHub Copilot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR changes CoreCLR QCall exception propagation by routing exceptions thrown inside BEGIN_QCALL/END_QCALL through an out QCallException status value that is custom-marshalled back to managed code. The native side captures throwables onto the managed Thread, and the managed marshaller rethrows during unmarshalling, while leaving non-BEGIN_QCALL QCalls on the prior behavior.

Changes:

  • Introduce QCallException + QCallExceptionMarshaller and update managed QCall signatures to include an out QCallException for BEGIN_QCALL-based QCalls.
  • Update native QCall entrypoints and exception macros to capture exceptions instead of dispatching/rethrowing directly across the boundary.
  • Add the per-thread storage needed to temporarily root the captured throwable and rethrow it from managed unmarshalling.

Reviewed changes

Copilot reviewed 170 out of 170 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiFinalizerScheduler.csAdd conditional out QCallException to WASI finalizer QCalls.
src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.csPass conditional out _ to updated reentrant wait QCall.
src/libraries/System.Private.CoreLib/src/System/Threading/Thread.csUpdate CurrentOSThreadId QCall invocation to include conditional out _.
src/libraries/System.Private.CoreLib/src/System/Threading/Lock.csUse updated Environment.GetProcessorCount signature under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.csThread out QCallException through multiple tracing/ALC QCalls.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.csAdd out QCallException to QCalls used by type map cache population/lookups.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCMarshal.csPass out _ to updated ObjC marshal QCalls.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NoCom.csExtend internal signature with conditional out _ under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.csCall updated RegisterIsRootedCallback under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.csUpdate tracing QCall invocations to include out _.
src/libraries/System.Private.CoreLib/src/System/Environment.csUpdate ProcessorCount initialization to use CORECLR QCall signature.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/XplatEventLogger.csAdd out QCallException plumbing to xplat EventSource QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventProvider.csAdd conditional out _ to EventPipeInternal QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.Threads.csAdd conditional out _ to EventPipe session wait/disable QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.csAdd conditional out _ to EventPipe dispatcher QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipe.csAdd conditional out _ to EventPipe enable QCall.
src/libraries/System.Private.CoreLib/src/System/AppContext.csAdd conditional out _ to host property QCall path.
src/coreclr/vm/weakreferencenative.hAdd QCallException* to COM weak ref QCall signatures.
src/coreclr/vm/weakreferencenative.cppAdd QCallException* parameter to COM weak ref QCall implementations.
src/coreclr/vm/wasm/entrypoints.hAdd QCallException* to WASI finalizer exported entrypoints.
src/coreclr/vm/versionresilienthashcode.hAdd QCallException* to version-resilient hash QCall signature.
src/coreclr/vm/versionresilienthashcode.cppAdd QCallException* to version-resilient hash QCall implementation.
src/coreclr/vm/varargsnative.hAdd QCallException* to varargs iterator QCall signatures.
src/coreclr/vm/varargsnative.cppAdd QCallException* to varargs iterator QCall implementations.
src/coreclr/vm/unsafeaccessors.cppAdd QCallException* to unsafe accessor resolver QCall.
src/coreclr/vm/tailcallhelp.hAdd QCallException* to tailcall arg buffer allocation QCall.
src/coreclr/vm/tailcallhelp.cppAdd QCallException* to tailcall arg buffer allocation QCall implementation.
src/coreclr/vm/stubhelpers.hAdd QCallException* to multiple interop/stub helper QCall signatures.
src/coreclr/vm/stackwalk.hAdd QCallException* to AppendExceptionStackFrame QCall signature.
src/coreclr/vm/reflectioninvocation.hAdd QCallException* to reflection invocation QCall signatures.
src/coreclr/vm/qcall.hUpdate QCall guidance/comments and route BEGIN/END macros to capture dispatcher.
src/coreclr/vm/qcall.cppAdd QCallException::SetThrowable implementation and thread rooting logic.
src/coreclr/vm/olevariant.hAdd QCallException* to VARIANT record conversion QCall signature.
src/coreclr/vm/olevariant.cppAdd QCallException* to VARIANT record conversion QCall implementation.
src/coreclr/vm/object.hAdd m_QCallException field + setter on ThreadBaseObject.
src/coreclr/vm/nativelibrarynative.hAdd QCallException* to NativeLibrary load-by-name QCall signature.
src/coreclr/vm/nativelibrarynative.cppAdd QCallException* to NativeLibrary load-by-name QCall implementation.
src/coreclr/vm/nativeeventsource.hAdd QCallException* to EventSource/perf tracing QCall signatures.
src/coreclr/vm/multicorejit.hAdd QCallException* to MultiCoreJIT QCall signatures.
src/coreclr/vm/multicorejit.cppAdd QCallException* to MultiCoreJIT QCall implementations.
src/coreclr/vm/mlinfo.cppAdd QCallException* to StubHelpers_CreateCustomMarshaler QCall implementation.
src/coreclr/vm/method.hppAdd QCallException* to unsafe accessor resolver declaration.
src/coreclr/vm/managedmdimport.hppAdd QCallException* to metadata enum QCall signature.
src/coreclr/vm/managedmdimport.cppAdd QCallException* to metadata enum QCall implementation.
src/coreclr/vm/loaderallocator.hppAdd QCallException* to loader allocator destroy QCall signature.
src/coreclr/vm/loaderallocator.cppAdd QCallException* to loader allocator destroy QCall implementation.
src/coreclr/vm/JitQCallHelpers.hAdd QCallException* to JIT helper QCall signatures.
src/coreclr/vm/jithelpers.cppAdd QCallException* to JIT helper QCalls and adjust handler installation in rare paths.
src/coreclr/vm/interoplibinterface.hAdd QCallException* to ObjC/Java marshal QCall signatures.
src/coreclr/vm/interoplibinterface_objc.cppAdd QCallException* to ObjC marshal QCall implementations.
src/coreclr/vm/interoplibinterface_java.cppAdd QCallException* to Java marshal QCall implementations.
src/coreclr/vm/interoplibinterface_comwrappers.hAdd QCallException* to ComWrappers QCall signatures.
src/coreclr/vm/interoplibinterface_comwrappers.cppAdd QCallException* to ComWrappers QCall implementations.
src/coreclr/vm/ilmarshalers.hAdd QCallException* to safe array marshaler QCall signatures.
src/coreclr/vm/ilmarshalers.cppAdd QCallException* to safe array marshaler QCall implementations.
src/coreclr/vm/finalizerthread.cppAdd QCallException* to WASI finalizer QCall implementations.
src/coreclr/vm/exceptmacros.hIntroduce QCallException native struct and capture-dispatcher exception macros.
src/coreclr/vm/exceptionhandlingqcalls.hAdd QCallException* to exception-handling-related QCall signatures.
src/coreclr/vm/exceptionhandling.cppAdd QCallException* to exception-handling-related QCall implementations.
src/coreclr/vm/excep.cppAdd QCallException* to message QCalls and introduce QCall-specific unwind helper.
src/coreclr/vm/eventpipeinternal.hAdd QCallException* to EventPipeInternal QCall signatures.
src/coreclr/vm/eventpipeinternal.cppAdd QCallException* to EventPipeInternal QCall implementations.
src/coreclr/vm/debugdebugger.hAdd QCallException* to debugger/stacktrace QCall signatures.
src/coreclr/vm/debugdebugger.cppAdd QCallException* to debugger/stacktrace QCall implementations.
src/coreclr/vm/customattribute.hAdd QCallException* to custom attribute QCall signatures.
src/coreclr/vm/customattribute.cppAdd QCallException* to custom attribute QCall implementations.
src/coreclr/vm/corelib.hAdd corelib field mapping for new thread _qcallException field.
src/coreclr/vm/coreassemblyspec.cppAdd QCallException* to assembly spec initialization QCall signature.
src/coreclr/vm/comsynchronizable.hAdd QCallException* to thread/lock-related QCall signatures.
src/coreclr/vm/comsynchronizable.cppAdd QCallException* to thread/lock-related QCall implementations.
src/coreclr/vm/commodule.hAdd QCallException* to Reflection.Emit module builder QCall signatures.
src/coreclr/vm/commodule.cppAdd QCallException* to Reflection.Emit module builder QCall implementations.
src/coreclr/vm/comdependenthandle.hAdd QCallException* to dependent handle QCall signatures.
src/coreclr/vm/comdependenthandle.cppAdd QCallException* to dependent handle QCall implementations.
src/coreclr/vm/comdelegate.hAdd QCallException* to delegate-related QCall signatures.
src/coreclr/vm/comdelegate.cppAdd QCallException* to delegate-related QCall implementations.
src/coreclr/vm/clrconfignative.hAdd QCallException* to CLR config QCall signature.
src/coreclr/vm/clrconfignative.cppAdd QCallException* to CLR config QCall implementation.
src/coreclr/vm/arraynative.hAdd QCallException* to array creation/ctor QCall signatures.
src/coreclr/vm/arraynative.cppAdd QCallException* to array creation/ctor QCall implementations.
src/coreclr/vm/appdomainnative.hppAdd QCallException* to AppDomain/AppContext/string intern QCall signatures.
src/coreclr/vm/appdomainnative.cppAdd QCallException* to AppDomain/AppContext/string intern QCall implementations.
src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csprojInclude new QCallException.cs in CoreCLR CoreLib build.
src/coreclr/System.Private.CoreLib/src/System/Variant.csAdd out QCallException to COM VARIANT-related QCalls.
src/coreclr/System.Private.CoreLib/src/System/ValueType.csAdd out QCallException to ValueType helper QCalls.
src/coreclr/System.Private.CoreLib/src/System/TypeLoadException.CoreCLR.csAdd out QCallException to message-formatting QCall.
src/coreclr/System.Private.CoreLib/src/System/Threading/ObjectHeader.CoreCLR.csAdd out QCallException to lock-object QCall.
src/coreclr/System.Private.CoreLib/src/System/String.CoreCLR.csAdd out QCallException to string intern/is-interned QCalls.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CreateUninitializedCache.CoreCLR.csAdd out QCallException to create-uninitialized info QCall.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.csAdd out QCallException to multiple RuntimeType/reflection QCalls.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.BoxCache.csAdd out QCallException to boxing cache QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/TrackerObjectManager.CoreCLR.csAdd out QCallException to wrapper-cache registration QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.CoreCLR.csAdd out QCallException to ObjC marshal QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.CoreCLR.csAdd out QCallException to NativeLibrary load QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/JavaMarshal.CoreCLR.csAdd out QCallException to Java marshal QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.csAdd out QCallException to GCHandle QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.CoreCLR.csAdd out QCallException to ComWrappers QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.csAdd out QCallException to EH-related QCall internal calls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/DependentHandle.csAdd out QCallException to dependent handle QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/ControlledExecution.CoreCLR.csAdd out QCallException to ThreadNative_Abort QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/VirtualDispatchHelpers.csAdd out QCallException to virtual dispatch resolver QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/StaticsHelpers.csAdd out QCallException to thread statics QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallException.csIntroduce managed QCall exception status type + custom marshaller.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/InitHelpers.csAdd out QCallException to init-class QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/GenericsHelpers.csAdd out QCallException to generic handle worker QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.csAdd out QCallException to cast helper QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.csAdd out QCallException to async continuation QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.CoreCLR.csAdd out QCallException to unsafe accessor resolver QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.csAdd out QCallException to module name/types QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.csAdd out QCallException to custom attribute QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.csAdd out QCallException to GetCurrentMethod QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.csAdd out QCallException to metadata update QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.csAdd out QCallException to raw metadata QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.csAdd out QCallException to metadata import enum QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/LoaderAllocator.csAdd out QCallException to loader allocator destroy QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeParameterBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.csAdd out QCallException to dynamic assembly creation QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.CoreCLR.csAdd out _ to compile-method QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.csAdd out _ to destroy handle QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/Reflection/AssemblyName.CoreCLR.csAdd out QCallException to assembly spec init QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.csAdd out QCallException to assembly lookup QCalls.
src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.csAdd out _ to clone allocation QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/IO/Stream.CoreCLR.csAdd out QCallException to overridden-check QCall.
src/coreclr/System.Private.CoreLib/src/System/IO/FileLoadException.CoreCLR.csAdd out QCallException to file load message QCalls.
src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.csAdd out QCallException to exception-related QCalls.
src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.csAdd out QCallException to Environment QCalls (Exit/FailFast/GetProcessorCount).
src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.csAdd out QCallException to enum values/names QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreCLR.csAdd out QCallException to stack trace frames QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrame.CoreCLR.csAdd out QCallException to method-desc-from-IP QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Debugger.csAdd out QCallException to debugger break/notification QCalls.
src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.csAdd out QCallException to delegate binding/construct QCalls.
src/coreclr/System.Private.CoreLib/src/System/ComAwareWeakReference.CoreCLR.csAdd out QCallException to COM weak ref QCalls.
src/coreclr/System.Private.CoreLib/src/System/CLRConfig.csAdd out QCallException to CLR config QCall.
src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.csAdd out QCallException to array creation/ctor QCalls.
src/coreclr/System.Private.CoreLib/src/System/ArgIterator.csAdd out QCallException to varargs iterator QCalls.
src/coreclr/System.Private.CoreLib/src/System/AppContext.CoreCLR.csAdd out QCallException to host property QCall.
src/coreclr/System.Private.CoreLib/src/Internal/VersionResilientHashCode.CoreCLR.csAdd out QCallException to version-resilient hash QCall.
src/coreclr/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.csAdd out QCallException to throw-helper QCalls.
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.csAdd conditional out _ to EH QCall internal calls.
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.csAdd conditional out _ to EH QCall internal calls.

Comment threadsrc/coreclr/vm/exceptmacros.h
@azure-pipelines

Copy link
Copy Markdown
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.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
CopilotAI review requested due to automatic review settings August 18, 2026 00:15

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 170 out of 170 changed files in this pull request and generated 1 comment.

CopilotAI review requested due to automatic review settings August 18, 2026 19:45

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 171 out of 171 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallException.cs:26

  • QCallExceptionMarshaller.FromUnmanaged only treats exceptionPending == 1 as an exception. Since the native representation is an INT32 flag, using a non-zero check (!= 0) is more robust against future expansion or accidental non-1 values, and avoids silently skipping exception propagation if the flag is ever set to another non-zero value.

Add managed forwarding overloads for helpers invoked by native IL stub generation so their CoreLib binder signatures remain stable after adding QCall exception parameters.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
Clean up accidental formatting and redundant conditionals introduced by the QCall migration. Make the COM stack trace assertion tolerate non-frame and additional lines while preserving frame-order validation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 31769fe8-86a0-4379-91f3-5353097eb47c
CopilotAI review requested due to automatic review settings September 1, 2026 19:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs:794

  • GetMethod(...) can return null, which would make failures here surface as a NullReferenceException later (or even pass silently if callers change). Since this helper is only for tests, it’s better to assert the MethodInfo is found so failures are clear and localized.
    src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallExceptionStatus.cs:45
  • The <= 2 check is a protocol constant that’s easy to lose track of when the native side evolves. Consider naming it as a constant so the intent is explicit and future updates are harder to miss.
  • Files reviewed: 155/155 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment threadsrc/tests/Interop/COM/NETClients/Primitives/StringTests.cs
Restore normal native QCall return values and pass exception status through a hidden final parameter.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 02:19

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It changes CoreCLR’s QCall exception-handling ABI and dispatch machinery across a wide set of managed and native entrypoints, making the regression surface too large to approve without focused human validation.

Review details
  • Files reviewed: 155/155 changed files
  • Comments generated: 1
  • Review effort level: Lite

@jkotas

jkotas commented Sep 2, 2026

Copy link
Copy Markdown
Member

Restore normal native QCall return values and pass exception status through a hidden final parameter.

Was there a specific problem with HiddenReturnValue that made you switch to HiddenLastParameter?

Comment threadsrc/coreclr/vm/excep.cpp Outdated
Comment threadsrc/coreclr/vm/exceptionhandling.cpp Outdated
Avoid relying on GET_THREAD and explicit MAKE_CURRENT_THREAD_AVAILABLE calls in QCall exception and reflection paths. Retrieve the current thread directly with GetThread, and correct the QCall casing in the exception rethrow helper name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 21:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The generated WASM callhelpers tables appear to drop the CompressionNative_CompressBound entry while managed code still imports it, which risks runtime entrypoint resolution failures on browser/WASI.

Review details

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallExceptionStatus.cs:45

  • QCallExceptionStatusMarshaller treats status values 1 and 2 as special cases, but the code currently uses a hard-coded magic number ("<= 2"). This makes the reserved-status contract harder to maintain and audit.

Consider introducing named constants (e.g., QCallOutOfMemoryExceptionStatus = 1, QCallStackOverflowExceptionStatus = 2, and/or MaxSpecialExceptionStatus = 2) and using them in the comparison.

src/coreclr/vm/wasm/browser/callhelpers-pinvoke.cpp:22

  • CompressionNative_CompressBound was removed from the generated WASM callhelpers P/Invoke table, but managed code still declares a [LibraryImport] for it (Interop.ZLib.compressBound). If this entry is missing at runtime, CompressionNative_CompressBound resolution is likely to fail (EntryPointNotFound / missing symbol) on browser/WASI.

Please ensure the callhelpers generator includes CompressionNative_CompressBound in both browser and WASI outputs (and the table counts match).

  • Files reviewed: 155/155 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Keep the CompressionNative_CompressBound entry in the browser and WASI static P/Invoke tables and update their entry counts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 21:58

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The marshaller’s special-status handling can preserve stale OutOfMemoryException stack traces due to reuse of the preallocated instance, which should be corrected before merging.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 155/155 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment on lines +41 to +61
Exception exception;
if ((nuint)status <= 2)
{
exception = Thread.GetQCallSpecialException(status);
}
else
{
GCHandle exceptionHandle = GCHandle.FromIntPtr(status);
try
{
exception = (Exception)exceptionHandle.Target!;
}
finally
{
exceptionHandle.Free();
}
}

// Throw during unmarshalling so QCall exception propagation remains as close as
// possible to throwing directly from native code, as QCalls did previously.
ExceptionDispatchInfo.Throw(exception);
Comment on lines +60 to +65
internal static Exception GetQCallSpecialException(nint status)
{
Exception? exception = null;
GetQCallSpecialException(status, ObjectHandleOnStack.Create(ref exception));
return exception!;
}
// [DllImport(JitHelpers.QCall, EntryPoint = "FooNative_Bar", CharSet = CharSet.Unicode)]
// // QCalls should always be static extern.
// private static extern bool Bar(int flags, string inString, StringHandleOnStack retString);
// // All QCalls that use BEGIN_QCALL must use the hidden last parameter error handler.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also described in docs\design\coreclr\botr\corelib.md . Can we update that copy as well? (Or deduplicate the description by replacing one of the copies with a link.)

{
"System.StubHelpers.StubHelpers.ThrowInteropParamException",
"Server.Contract.IStringTesting.Reverse_LPWStr_OutAttr",
"Xunit.Assert.RecordException",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is likely to be unreliable with JIT stress when some of the methods can be aggressively inlined and no longer show up in the stacktrace.

@jkotas

Copy link
Copy Markdown
Member

LGTM otherwise

internal static unsafe partial class ThrowHelpers
{
[DoesNotReturn]
[ErrorHandler(typeof(QCallExceptionStatusMarshaller), ErrorLocation.HiddenLastParameter)]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Reposting earlier question since it got lost in the comment noise)

Was there a specific problem with HiddenReturnValue that made you switch to HiddenLastParameter?

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@davidwrighton@jkotas@AustinWise
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Route QCall exceptions through marshalling - #132420

Open
davidwrighton wants to merge 29 commits into
dotnet:mainfrom
davidwrighton:fixup_qcalls
Open

Route QCall exceptions through marshalling#132420
davidwrighton wants to merge 29 commits into
dotnet:mainfrom
davidwrighton:fixup_qcalls

Conversation

@davidwrighton

@davidwrightondavidwrighton commented Aug 17, 2026

Copy link
Copy Markdown
Member

Summary

  • add source-generated LibraryImport support for custom error handlers sourced from the native return value, final parameter, or injected hidden return/parameter locations
  • use the hidden-return error-handler form for CoreCLR QCalls that use BEGIN_QCALL / END_QCALL, while preserving their existing managed signatures
  • change migrated native QCall entry points to return QCallExceptionStatus and move ordinary native return values to explicit out parameters
  • capture exceptions at the QCall boundary and return either a reserved status for preallocated OutOfMemoryException / StackOverflowException instances or a handle to the exact managed exception
  • convert and rethrow the status during managed unmarshalling, preserving the original exception instance and stack trace and releasing exception handles after transfer
  • keep QCalls without the exception-handling macros on their existing ABI, and retain binder-visible helper signatures through managed wrappers where necessary
  • update native declarations and generated browser/WASI call helpers for the new QCall ABI

Testing

  • build.cmd clr+libs -c Checked
  • build.cmd mono+libs
  • build.cmd clr+libs -os browser -c Debug
  • build.cmd clr.corelib -os osx -a x64 -c Debug
  • build.cmd clr.runtime -a x86 -c Checked
  • src\tasks\WasmAppBuilder\generate-coreclr-helpers.cmd -c Debug
  • AmbiguousImplementationException runtime test, including repeated exception propagation
  • RuntimeHelpersTests.QCallExceptionStatus_ThrowsSpecialException
  • RuntimeHelpersTests.QCallExceptionStatus_ThrowsExactExceptionFromHandle
  • COM string marshalling tests covering preservation of the customer-visible exception stack trace
  • migration audit confirming only QCalls using BEGIN_QCALL were converted
  • git diff --check

Note

This pull request description was generated by GitHub Copilot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR changes CoreCLR QCall exception propagation by routing exceptions thrown inside BEGIN_QCALL/END_QCALL through an out QCallException status value that is custom-marshalled back to managed code. The native side captures throwables onto the managed Thread, and the managed marshaller rethrows during unmarshalling, while leaving non-BEGIN_QCALL QCalls on the prior behavior.

Changes:

  • Introduce QCallException + QCallExceptionMarshaller and update managed QCall signatures to include an out QCallException for BEGIN_QCALL-based QCalls.
  • Update native QCall entrypoints and exception macros to capture exceptions instead of dispatching/rethrowing directly across the boundary.
  • Add the per-thread storage needed to temporarily root the captured throwable and rethrow it from managed unmarshalling.

Reviewed changes

Copilot reviewed 170 out of 170 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiFinalizerScheduler.csAdd conditional out QCallException to WASI finalizer QCalls.
src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.csPass conditional out _ to updated reentrant wait QCall.
src/libraries/System.Private.CoreLib/src/System/Threading/Thread.csUpdate CurrentOSThreadId QCall invocation to include conditional out _.
src/libraries/System.Private.CoreLib/src/System/Threading/Lock.csUse updated Environment.GetProcessorCount signature under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.csThread out QCallException through multiple tracing/ALC QCalls.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.csAdd out QCallException to QCalls used by type map cache population/lookups.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCMarshal.csPass out _ to updated ObjC marshal QCalls.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NoCom.csExtend internal signature with conditional out _ under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.csCall updated RegisterIsRootedCallback under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.csUpdate tracing QCall invocations to include out _.
src/libraries/System.Private.CoreLib/src/System/Environment.csUpdate ProcessorCount initialization to use CORECLR QCall signature.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/XplatEventLogger.csAdd out QCallException plumbing to xplat EventSource QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventProvider.csAdd conditional out _ to EventPipeInternal QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.Threads.csAdd conditional out _ to EventPipe session wait/disable QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.csAdd conditional out _ to EventPipe dispatcher QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipe.csAdd conditional out _ to EventPipe enable QCall.
src/libraries/System.Private.CoreLib/src/System/AppContext.csAdd conditional out _ to host property QCall path.
src/coreclr/vm/weakreferencenative.hAdd QCallException* to COM weak ref QCall signatures.
src/coreclr/vm/weakreferencenative.cppAdd QCallException* parameter to COM weak ref QCall implementations.
src/coreclr/vm/wasm/entrypoints.hAdd QCallException* to WASI finalizer exported entrypoints.
src/coreclr/vm/versionresilienthashcode.hAdd QCallException* to version-resilient hash QCall signature.
src/coreclr/vm/versionresilienthashcode.cppAdd QCallException* to version-resilient hash QCall implementation.
src/coreclr/vm/varargsnative.hAdd QCallException* to varargs iterator QCall signatures.
src/coreclr/vm/varargsnative.cppAdd QCallException* to varargs iterator QCall implementations.
src/coreclr/vm/unsafeaccessors.cppAdd QCallException* to unsafe accessor resolver QCall.
src/coreclr/vm/tailcallhelp.hAdd QCallException* to tailcall arg buffer allocation QCall.
src/coreclr/vm/tailcallhelp.cppAdd QCallException* to tailcall arg buffer allocation QCall implementation.
src/coreclr/vm/stubhelpers.hAdd QCallException* to multiple interop/stub helper QCall signatures.
src/coreclr/vm/stackwalk.hAdd QCallException* to AppendExceptionStackFrame QCall signature.
src/coreclr/vm/reflectioninvocation.hAdd QCallException* to reflection invocation QCall signatures.
src/coreclr/vm/qcall.hUpdate QCall guidance/comments and route BEGIN/END macros to capture dispatcher.
src/coreclr/vm/qcall.cppAdd QCallException::SetThrowable implementation and thread rooting logic.
src/coreclr/vm/olevariant.hAdd QCallException* to VARIANT record conversion QCall signature.
src/coreclr/vm/olevariant.cppAdd QCallException* to VARIANT record conversion QCall implementation.
src/coreclr/vm/object.hAdd m_QCallException field + setter on ThreadBaseObject.
src/coreclr/vm/nativelibrarynative.hAdd QCallException* to NativeLibrary load-by-name QCall signature.
src/coreclr/vm/nativelibrarynative.cppAdd QCallException* to NativeLibrary load-by-name QCall implementation.
src/coreclr/vm/nativeeventsource.hAdd QCallException* to EventSource/perf tracing QCall signatures.
src/coreclr/vm/multicorejit.hAdd QCallException* to MultiCoreJIT QCall signatures.
src/coreclr/vm/multicorejit.cppAdd QCallException* to MultiCoreJIT QCall implementations.
src/coreclr/vm/mlinfo.cppAdd QCallException* to StubHelpers_CreateCustomMarshaler QCall implementation.
src/coreclr/vm/method.hppAdd QCallException* to unsafe accessor resolver declaration.
src/coreclr/vm/managedmdimport.hppAdd QCallException* to metadata enum QCall signature.
src/coreclr/vm/managedmdimport.cppAdd QCallException* to metadata enum QCall implementation.
src/coreclr/vm/loaderallocator.hppAdd QCallException* to loader allocator destroy QCall signature.
src/coreclr/vm/loaderallocator.cppAdd QCallException* to loader allocator destroy QCall implementation.
src/coreclr/vm/JitQCallHelpers.hAdd QCallException* to JIT helper QCall signatures.
src/coreclr/vm/jithelpers.cppAdd QCallException* to JIT helper QCalls and adjust handler installation in rare paths.
src/coreclr/vm/interoplibinterface.hAdd QCallException* to ObjC/Java marshal QCall signatures.
src/coreclr/vm/interoplibinterface_objc.cppAdd QCallException* to ObjC marshal QCall implementations.
src/coreclr/vm/interoplibinterface_java.cppAdd QCallException* to Java marshal QCall implementations.
src/coreclr/vm/interoplibinterface_comwrappers.hAdd QCallException* to ComWrappers QCall signatures.
src/coreclr/vm/interoplibinterface_comwrappers.cppAdd QCallException* to ComWrappers QCall implementations.
src/coreclr/vm/ilmarshalers.hAdd QCallException* to safe array marshaler QCall signatures.
src/coreclr/vm/ilmarshalers.cppAdd QCallException* to safe array marshaler QCall implementations.
src/coreclr/vm/finalizerthread.cppAdd QCallException* to WASI finalizer QCall implementations.
src/coreclr/vm/exceptmacros.hIntroduce QCallException native struct and capture-dispatcher exception macros.
src/coreclr/vm/exceptionhandlingqcalls.hAdd QCallException* to exception-handling-related QCall signatures.
src/coreclr/vm/exceptionhandling.cppAdd QCallException* to exception-handling-related QCall implementations.
src/coreclr/vm/excep.cppAdd QCallException* to message QCalls and introduce QCall-specific unwind helper.
src/coreclr/vm/eventpipeinternal.hAdd QCallException* to EventPipeInternal QCall signatures.
src/coreclr/vm/eventpipeinternal.cppAdd QCallException* to EventPipeInternal QCall implementations.
src/coreclr/vm/debugdebugger.hAdd QCallException* to debugger/stacktrace QCall signatures.
src/coreclr/vm/debugdebugger.cppAdd QCallException* to debugger/stacktrace QCall implementations.
src/coreclr/vm/customattribute.hAdd QCallException* to custom attribute QCall signatures.
src/coreclr/vm/customattribute.cppAdd QCallException* to custom attribute QCall implementations.
src/coreclr/vm/corelib.hAdd corelib field mapping for new thread _qcallException field.
src/coreclr/vm/coreassemblyspec.cppAdd QCallException* to assembly spec initialization QCall signature.
src/coreclr/vm/comsynchronizable.hAdd QCallException* to thread/lock-related QCall signatures.
src/coreclr/vm/comsynchronizable.cppAdd QCallException* to thread/lock-related QCall implementations.
src/coreclr/vm/commodule.hAdd QCallException* to Reflection.Emit module builder QCall signatures.
src/coreclr/vm/commodule.cppAdd QCallException* to Reflection.Emit module builder QCall implementations.
src/coreclr/vm/comdependenthandle.hAdd QCallException* to dependent handle QCall signatures.
src/coreclr/vm/comdependenthandle.cppAdd QCallException* to dependent handle QCall implementations.
src/coreclr/vm/comdelegate.hAdd QCallException* to delegate-related QCall signatures.
src/coreclr/vm/comdelegate.cppAdd QCallException* to delegate-related QCall implementations.
src/coreclr/vm/clrconfignative.hAdd QCallException* to CLR config QCall signature.
src/coreclr/vm/clrconfignative.cppAdd QCallException* to CLR config QCall implementation.
src/coreclr/vm/arraynative.hAdd QCallException* to array creation/ctor QCall signatures.
src/coreclr/vm/arraynative.cppAdd QCallException* to array creation/ctor QCall implementations.
src/coreclr/vm/appdomainnative.hppAdd QCallException* to AppDomain/AppContext/string intern QCall signatures.
src/coreclr/vm/appdomainnative.cppAdd QCallException* to AppDomain/AppContext/string intern QCall implementations.
src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csprojInclude new QCallException.cs in CoreCLR CoreLib build.
src/coreclr/System.Private.CoreLib/src/System/Variant.csAdd out QCallException to COM VARIANT-related QCalls.
src/coreclr/System.Private.CoreLib/src/System/ValueType.csAdd out QCallException to ValueType helper QCalls.
src/coreclr/System.Private.CoreLib/src/System/TypeLoadException.CoreCLR.csAdd out QCallException to message-formatting QCall.
src/coreclr/System.Private.CoreLib/src/System/Threading/ObjectHeader.CoreCLR.csAdd out QCallException to lock-object QCall.
src/coreclr/System.Private.CoreLib/src/System/String.CoreCLR.csAdd out QCallException to string intern/is-interned QCalls.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CreateUninitializedCache.CoreCLR.csAdd out QCallException to create-uninitialized info QCall.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.csAdd out QCallException to multiple RuntimeType/reflection QCalls.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.BoxCache.csAdd out QCallException to boxing cache QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/TrackerObjectManager.CoreCLR.csAdd out QCallException to wrapper-cache registration QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.CoreCLR.csAdd out QCallException to ObjC marshal QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.CoreCLR.csAdd out QCallException to NativeLibrary load QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/JavaMarshal.CoreCLR.csAdd out QCallException to Java marshal QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.csAdd out QCallException to GCHandle QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.CoreCLR.csAdd out QCallException to ComWrappers QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.csAdd out QCallException to EH-related QCall internal calls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/DependentHandle.csAdd out QCallException to dependent handle QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/ControlledExecution.CoreCLR.csAdd out QCallException to ThreadNative_Abort QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/VirtualDispatchHelpers.csAdd out QCallException to virtual dispatch resolver QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/StaticsHelpers.csAdd out QCallException to thread statics QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallException.csIntroduce managed QCall exception status type + custom marshaller.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/InitHelpers.csAdd out QCallException to init-class QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/GenericsHelpers.csAdd out QCallException to generic handle worker QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.csAdd out QCallException to cast helper QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.csAdd out QCallException to async continuation QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.CoreCLR.csAdd out QCallException to unsafe accessor resolver QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.csAdd out QCallException to module name/types QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.csAdd out QCallException to custom attribute QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.csAdd out QCallException to GetCurrentMethod QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.csAdd out QCallException to metadata update QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.csAdd out QCallException to raw metadata QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.csAdd out QCallException to metadata import enum QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/LoaderAllocator.csAdd out QCallException to loader allocator destroy QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeParameterBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.csAdd out QCallException to dynamic assembly creation QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.CoreCLR.csAdd out _ to compile-method QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.csAdd out _ to destroy handle QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/Reflection/AssemblyName.CoreCLR.csAdd out QCallException to assembly spec init QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.csAdd out QCallException to assembly lookup QCalls.
src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.csAdd out _ to clone allocation QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/IO/Stream.CoreCLR.csAdd out QCallException to overridden-check QCall.
src/coreclr/System.Private.CoreLib/src/System/IO/FileLoadException.CoreCLR.csAdd out QCallException to file load message QCalls.
src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.csAdd out QCallException to exception-related QCalls.
src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.csAdd out QCallException to Environment QCalls (Exit/FailFast/GetProcessorCount).
src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.csAdd out QCallException to enum values/names QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreCLR.csAdd out QCallException to stack trace frames QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrame.CoreCLR.csAdd out QCallException to method-desc-from-IP QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Debugger.csAdd out QCallException to debugger break/notification QCalls.
src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.csAdd out QCallException to delegate binding/construct QCalls.
src/coreclr/System.Private.CoreLib/src/System/ComAwareWeakReference.CoreCLR.csAdd out QCallException to COM weak ref QCalls.
src/coreclr/System.Private.CoreLib/src/System/CLRConfig.csAdd out QCallException to CLR config QCall.
src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.csAdd out QCallException to array creation/ctor QCalls.
src/coreclr/System.Private.CoreLib/src/System/ArgIterator.csAdd out QCallException to varargs iterator QCalls.
src/coreclr/System.Private.CoreLib/src/System/AppContext.CoreCLR.csAdd out QCallException to host property QCall.
src/coreclr/System.Private.CoreLib/src/Internal/VersionResilientHashCode.CoreCLR.csAdd out QCallException to version-resilient hash QCall.
src/coreclr/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.csAdd out QCallException to throw-helper QCalls.
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.csAdd conditional out _ to EH QCall internal calls.
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.csAdd conditional out _ to EH QCall internal calls.

Comment threadsrc/coreclr/vm/exceptmacros.h
@azure-pipelines

Copy link
Copy Markdown
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.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
CopilotAI review requested due to automatic review settings August 18, 2026 00:15

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 170 out of 170 changed files in this pull request and generated 1 comment.

CopilotAI review requested due to automatic review settings August 18, 2026 19:45

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 171 out of 171 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallException.cs:26

  • QCallExceptionMarshaller.FromUnmanaged only treats exceptionPending == 1 as an exception. Since the native representation is an INT32 flag, using a non-zero check (!= 0) is more robust against future expansion or accidental non-1 values, and avoids silently skipping exception propagation if the flag is ever set to another non-zero value.

Add managed forwarding overloads for helpers invoked by native IL stub generation so their CoreLib binder signatures remain stable after adding QCall exception parameters.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
Clean up accidental formatting and redundant conditionals introduced by the QCall migration. Make the COM stack trace assertion tolerate non-frame and additional lines while preserving frame-order validation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 31769fe8-86a0-4379-91f3-5353097eb47c
CopilotAI review requested due to automatic review settings September 1, 2026 19:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs:794

  • GetMethod(...) can return null, which would make failures here surface as a NullReferenceException later (or even pass silently if callers change). Since this helper is only for tests, it’s better to assert the MethodInfo is found so failures are clear and localized.
    src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallExceptionStatus.cs:45
  • The <= 2 check is a protocol constant that’s easy to lose track of when the native side evolves. Consider naming it as a constant so the intent is explicit and future updates are harder to miss.
  • Files reviewed: 155/155 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment threadsrc/tests/Interop/COM/NETClients/Primitives/StringTests.cs
Restore normal native QCall return values and pass exception status through a hidden final parameter.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 02:19

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It changes CoreCLR’s QCall exception-handling ABI and dispatch machinery across a wide set of managed and native entrypoints, making the regression surface too large to approve without focused human validation.

Review details
  • Files reviewed: 155/155 changed files
  • Comments generated: 1
  • Review effort level: Lite

@jkotas

jkotas commented Sep 2, 2026

Copy link
Copy Markdown
Member

Restore normal native QCall return values and pass exception status through a hidden final parameter.

Was there a specific problem with HiddenReturnValue that made you switch to HiddenLastParameter?

Comment threadsrc/coreclr/vm/excep.cpp Outdated
Comment threadsrc/coreclr/vm/exceptionhandling.cpp Outdated
Avoid relying on GET_THREAD and explicit MAKE_CURRENT_THREAD_AVAILABLE calls in QCall exception and reflection paths. Retrieve the current thread directly with GetThread, and correct the QCall casing in the exception rethrow helper name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 21:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The generated WASM callhelpers tables appear to drop the CompressionNative_CompressBound entry while managed code still imports it, which risks runtime entrypoint resolution failures on browser/WASI.

Review details

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallExceptionStatus.cs:45

  • QCallExceptionStatusMarshaller treats status values 1 and 2 as special cases, but the code currently uses a hard-coded magic number ("<= 2"). This makes the reserved-status contract harder to maintain and audit.

Consider introducing named constants (e.g., QCallOutOfMemoryExceptionStatus = 1, QCallStackOverflowExceptionStatus = 2, and/or MaxSpecialExceptionStatus = 2) and using them in the comparison.

src/coreclr/vm/wasm/browser/callhelpers-pinvoke.cpp:22

  • CompressionNative_CompressBound was removed from the generated WASM callhelpers P/Invoke table, but managed code still declares a [LibraryImport] for it (Interop.ZLib.compressBound). If this entry is missing at runtime, CompressionNative_CompressBound resolution is likely to fail (EntryPointNotFound / missing symbol) on browser/WASI.

Please ensure the callhelpers generator includes CompressionNative_CompressBound in both browser and WASI outputs (and the table counts match).

  • Files reviewed: 155/155 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Keep the CompressionNative_CompressBound entry in the browser and WASI static P/Invoke tables and update their entry counts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 21:58

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The marshaller’s special-status handling can preserve stale OutOfMemoryException stack traces due to reuse of the preallocated instance, which should be corrected before merging.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 155/155 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment on lines +41 to +61
Exception exception;
if ((nuint)status <= 2)
{
exception = Thread.GetQCallSpecialException(status);
}
else
{
GCHandle exceptionHandle = GCHandle.FromIntPtr(status);
try
{
exception = (Exception)exceptionHandle.Target!;
}
finally
{
exceptionHandle.Free();
}
}

// Throw during unmarshalling so QCall exception propagation remains as close as
// possible to throwing directly from native code, as QCalls did previously.
ExceptionDispatchInfo.Throw(exception);
Comment on lines +60 to +65
internal static Exception GetQCallSpecialException(nint status)
{
Exception? exception = null;
GetQCallSpecialException(status, ObjectHandleOnStack.Create(ref exception));
return exception!;
}
// [DllImport(JitHelpers.QCall, EntryPoint = "FooNative_Bar", CharSet = CharSet.Unicode)]
// // QCalls should always be static extern.
// private static extern bool Bar(int flags, string inString, StringHandleOnStack retString);
// // All QCalls that use BEGIN_QCALL must use the hidden last parameter error handler.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also described in docs\design\coreclr\botr\corelib.md . Can we update that copy as well? (Or deduplicate the description by replacing one of the copies with a link.)

{
"System.StubHelpers.StubHelpers.ThrowInteropParamException",
"Server.Contract.IStringTesting.Reverse_LPWStr_OutAttr",
"Xunit.Assert.RecordException",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is likely to be unreliable with JIT stress when some of the methods can be aggressively inlined and no longer show up in the stacktrace.

@jkotas

Copy link
Copy Markdown
Member

LGTM otherwise

internal static unsafe partial class ThrowHelpers
{
[DoesNotReturn]
[ErrorHandler(typeof(QCallExceptionStatusMarshaller), ErrorLocation.HiddenLastParameter)]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Reposting earlier question since it got lost in the comment noise)

Was there a specific problem with HiddenReturnValue that made you switch to HiddenLastParameter?

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@davidwrighton@jkotas@AustinWise
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Route QCall exceptions through marshalling - #132420

Open
davidwrighton wants to merge 29 commits into
dotnet:mainfrom
davidwrighton:fixup_qcalls
Open

Route QCall exceptions through marshalling#132420
davidwrighton wants to merge 29 commits into
dotnet:mainfrom
davidwrighton:fixup_qcalls

Conversation

@davidwrighton

@davidwrightondavidwrighton commented Aug 17, 2026

Copy link
Copy Markdown
Member

Summary

  • add source-generated LibraryImport support for custom error handlers sourced from the native return value, final parameter, or injected hidden return/parameter locations
  • use the hidden-return error-handler form for CoreCLR QCalls that use BEGIN_QCALL / END_QCALL, while preserving their existing managed signatures
  • change migrated native QCall entry points to return QCallExceptionStatus and move ordinary native return values to explicit out parameters
  • capture exceptions at the QCall boundary and return either a reserved status for preallocated OutOfMemoryException / StackOverflowException instances or a handle to the exact managed exception
  • convert and rethrow the status during managed unmarshalling, preserving the original exception instance and stack trace and releasing exception handles after transfer
  • keep QCalls without the exception-handling macros on their existing ABI, and retain binder-visible helper signatures through managed wrappers where necessary
  • update native declarations and generated browser/WASI call helpers for the new QCall ABI

Testing

  • build.cmd clr+libs -c Checked
  • build.cmd mono+libs
  • build.cmd clr+libs -os browser -c Debug
  • build.cmd clr.corelib -os osx -a x64 -c Debug
  • build.cmd clr.runtime -a x86 -c Checked
  • src\tasks\WasmAppBuilder\generate-coreclr-helpers.cmd -c Debug
  • AmbiguousImplementationException runtime test, including repeated exception propagation
  • RuntimeHelpersTests.QCallExceptionStatus_ThrowsSpecialException
  • RuntimeHelpersTests.QCallExceptionStatus_ThrowsExactExceptionFromHandle
  • COM string marshalling tests covering preservation of the customer-visible exception stack trace
  • migration audit confirming only QCalls using BEGIN_QCALL were converted
  • git diff --check

Note

This pull request description was generated by GitHub Copilot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR changes CoreCLR QCall exception propagation by routing exceptions thrown inside BEGIN_QCALL/END_QCALL through an out QCallException status value that is custom-marshalled back to managed code. The native side captures throwables onto the managed Thread, and the managed marshaller rethrows during unmarshalling, while leaving non-BEGIN_QCALL QCalls on the prior behavior.

Changes:

  • Introduce QCallException + QCallExceptionMarshaller and update managed QCall signatures to include an out QCallException for BEGIN_QCALL-based QCalls.
  • Update native QCall entrypoints and exception macros to capture exceptions instead of dispatching/rethrowing directly across the boundary.
  • Add the per-thread storage needed to temporarily root the captured throwable and rethrow it from managed unmarshalling.

Reviewed changes

Copilot reviewed 170 out of 170 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiFinalizerScheduler.csAdd conditional out QCallException to WASI finalizer QCalls.
src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.csPass conditional out _ to updated reentrant wait QCall.
src/libraries/System.Private.CoreLib/src/System/Threading/Thread.csUpdate CurrentOSThreadId QCall invocation to include conditional out _.
src/libraries/System.Private.CoreLib/src/System/Threading/Lock.csUse updated Environment.GetProcessorCount signature under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.csThread out QCallException through multiple tracing/ALC QCalls.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.csAdd out QCallException to QCalls used by type map cache population/lookups.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCMarshal.csPass out _ to updated ObjC marshal QCalls.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NoCom.csExtend internal signature with conditional out _ under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.csCall updated RegisterIsRootedCallback under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.csUpdate tracing QCall invocations to include out _.
src/libraries/System.Private.CoreLib/src/System/Environment.csUpdate ProcessorCount initialization to use CORECLR QCall signature.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/XplatEventLogger.csAdd out QCallException plumbing to xplat EventSource QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventProvider.csAdd conditional out _ to EventPipeInternal QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.Threads.csAdd conditional out _ to EventPipe session wait/disable QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.csAdd conditional out _ to EventPipe dispatcher QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipe.csAdd conditional out _ to EventPipe enable QCall.
src/libraries/System.Private.CoreLib/src/System/AppContext.csAdd conditional out _ to host property QCall path.
src/coreclr/vm/weakreferencenative.hAdd QCallException* to COM weak ref QCall signatures.
src/coreclr/vm/weakreferencenative.cppAdd QCallException* parameter to COM weak ref QCall implementations.
src/coreclr/vm/wasm/entrypoints.hAdd QCallException* to WASI finalizer exported entrypoints.
src/coreclr/vm/versionresilienthashcode.hAdd QCallException* to version-resilient hash QCall signature.
src/coreclr/vm/versionresilienthashcode.cppAdd QCallException* to version-resilient hash QCall implementation.
src/coreclr/vm/varargsnative.hAdd QCallException* to varargs iterator QCall signatures.
src/coreclr/vm/varargsnative.cppAdd QCallException* to varargs iterator QCall implementations.
src/coreclr/vm/unsafeaccessors.cppAdd QCallException* to unsafe accessor resolver QCall.
src/coreclr/vm/tailcallhelp.hAdd QCallException* to tailcall arg buffer allocation QCall.
src/coreclr/vm/tailcallhelp.cppAdd QCallException* to tailcall arg buffer allocation QCall implementation.
src/coreclr/vm/stubhelpers.hAdd QCallException* to multiple interop/stub helper QCall signatures.
src/coreclr/vm/stackwalk.hAdd QCallException* to AppendExceptionStackFrame QCall signature.
src/coreclr/vm/reflectioninvocation.hAdd QCallException* to reflection invocation QCall signatures.
src/coreclr/vm/qcall.hUpdate QCall guidance/comments and route BEGIN/END macros to capture dispatcher.
src/coreclr/vm/qcall.cppAdd QCallException::SetThrowable implementation and thread rooting logic.
src/coreclr/vm/olevariant.hAdd QCallException* to VARIANT record conversion QCall signature.
src/coreclr/vm/olevariant.cppAdd QCallException* to VARIANT record conversion QCall implementation.
src/coreclr/vm/object.hAdd m_QCallException field + setter on ThreadBaseObject.
src/coreclr/vm/nativelibrarynative.hAdd QCallException* to NativeLibrary load-by-name QCall signature.
src/coreclr/vm/nativelibrarynative.cppAdd QCallException* to NativeLibrary load-by-name QCall implementation.
src/coreclr/vm/nativeeventsource.hAdd QCallException* to EventSource/perf tracing QCall signatures.
src/coreclr/vm/multicorejit.hAdd QCallException* to MultiCoreJIT QCall signatures.
src/coreclr/vm/multicorejit.cppAdd QCallException* to MultiCoreJIT QCall implementations.
src/coreclr/vm/mlinfo.cppAdd QCallException* to StubHelpers_CreateCustomMarshaler QCall implementation.
src/coreclr/vm/method.hppAdd QCallException* to unsafe accessor resolver declaration.
src/coreclr/vm/managedmdimport.hppAdd QCallException* to metadata enum QCall signature.
src/coreclr/vm/managedmdimport.cppAdd QCallException* to metadata enum QCall implementation.
src/coreclr/vm/loaderallocator.hppAdd QCallException* to loader allocator destroy QCall signature.
src/coreclr/vm/loaderallocator.cppAdd QCallException* to loader allocator destroy QCall implementation.
src/coreclr/vm/JitQCallHelpers.hAdd QCallException* to JIT helper QCall signatures.
src/coreclr/vm/jithelpers.cppAdd QCallException* to JIT helper QCalls and adjust handler installation in rare paths.
src/coreclr/vm/interoplibinterface.hAdd QCallException* to ObjC/Java marshal QCall signatures.
src/coreclr/vm/interoplibinterface_objc.cppAdd QCallException* to ObjC marshal QCall implementations.
src/coreclr/vm/interoplibinterface_java.cppAdd QCallException* to Java marshal QCall implementations.
src/coreclr/vm/interoplibinterface_comwrappers.hAdd QCallException* to ComWrappers QCall signatures.
src/coreclr/vm/interoplibinterface_comwrappers.cppAdd QCallException* to ComWrappers QCall implementations.
src/coreclr/vm/ilmarshalers.hAdd QCallException* to safe array marshaler QCall signatures.
src/coreclr/vm/ilmarshalers.cppAdd QCallException* to safe array marshaler QCall implementations.
src/coreclr/vm/finalizerthread.cppAdd QCallException* to WASI finalizer QCall implementations.
src/coreclr/vm/exceptmacros.hIntroduce QCallException native struct and capture-dispatcher exception macros.
src/coreclr/vm/exceptionhandlingqcalls.hAdd QCallException* to exception-handling-related QCall signatures.
src/coreclr/vm/exceptionhandling.cppAdd QCallException* to exception-handling-related QCall implementations.
src/coreclr/vm/excep.cppAdd QCallException* to message QCalls and introduce QCall-specific unwind helper.
src/coreclr/vm/eventpipeinternal.hAdd QCallException* to EventPipeInternal QCall signatures.
src/coreclr/vm/eventpipeinternal.cppAdd QCallException* to EventPipeInternal QCall implementations.
src/coreclr/vm/debugdebugger.hAdd QCallException* to debugger/stacktrace QCall signatures.
src/coreclr/vm/debugdebugger.cppAdd QCallException* to debugger/stacktrace QCall implementations.
src/coreclr/vm/customattribute.hAdd QCallException* to custom attribute QCall signatures.
src/coreclr/vm/customattribute.cppAdd QCallException* to custom attribute QCall implementations.
src/coreclr/vm/corelib.hAdd corelib field mapping for new thread _qcallException field.
src/coreclr/vm/coreassemblyspec.cppAdd QCallException* to assembly spec initialization QCall signature.
src/coreclr/vm/comsynchronizable.hAdd QCallException* to thread/lock-related QCall signatures.
src/coreclr/vm/comsynchronizable.cppAdd QCallException* to thread/lock-related QCall implementations.
src/coreclr/vm/commodule.hAdd QCallException* to Reflection.Emit module builder QCall signatures.
src/coreclr/vm/commodule.cppAdd QCallException* to Reflection.Emit module builder QCall implementations.
src/coreclr/vm/comdependenthandle.hAdd QCallException* to dependent handle QCall signatures.
src/coreclr/vm/comdependenthandle.cppAdd QCallException* to dependent handle QCall implementations.
src/coreclr/vm/comdelegate.hAdd QCallException* to delegate-related QCall signatures.
src/coreclr/vm/comdelegate.cppAdd QCallException* to delegate-related QCall implementations.
src/coreclr/vm/clrconfignative.hAdd QCallException* to CLR config QCall signature.
src/coreclr/vm/clrconfignative.cppAdd QCallException* to CLR config QCall implementation.
src/coreclr/vm/arraynative.hAdd QCallException* to array creation/ctor QCall signatures.
src/coreclr/vm/arraynative.cppAdd QCallException* to array creation/ctor QCall implementations.
src/coreclr/vm/appdomainnative.hppAdd QCallException* to AppDomain/AppContext/string intern QCall signatures.
src/coreclr/vm/appdomainnative.cppAdd QCallException* to AppDomain/AppContext/string intern QCall implementations.
src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csprojInclude new QCallException.cs in CoreCLR CoreLib build.
src/coreclr/System.Private.CoreLib/src/System/Variant.csAdd out QCallException to COM VARIANT-related QCalls.
src/coreclr/System.Private.CoreLib/src/System/ValueType.csAdd out QCallException to ValueType helper QCalls.
src/coreclr/System.Private.CoreLib/src/System/TypeLoadException.CoreCLR.csAdd out QCallException to message-formatting QCall.
src/coreclr/System.Private.CoreLib/src/System/Threading/ObjectHeader.CoreCLR.csAdd out QCallException to lock-object QCall.
src/coreclr/System.Private.CoreLib/src/System/String.CoreCLR.csAdd out QCallException to string intern/is-interned QCalls.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CreateUninitializedCache.CoreCLR.csAdd out QCallException to create-uninitialized info QCall.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.csAdd out QCallException to multiple RuntimeType/reflection QCalls.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.BoxCache.csAdd out QCallException to boxing cache QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/TrackerObjectManager.CoreCLR.csAdd out QCallException to wrapper-cache registration QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.CoreCLR.csAdd out QCallException to ObjC marshal QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.CoreCLR.csAdd out QCallException to NativeLibrary load QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/JavaMarshal.CoreCLR.csAdd out QCallException to Java marshal QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.csAdd out QCallException to GCHandle QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.CoreCLR.csAdd out QCallException to ComWrappers QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.csAdd out QCallException to EH-related QCall internal calls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/DependentHandle.csAdd out QCallException to dependent handle QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/ControlledExecution.CoreCLR.csAdd out QCallException to ThreadNative_Abort QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/VirtualDispatchHelpers.csAdd out QCallException to virtual dispatch resolver QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/StaticsHelpers.csAdd out QCallException to thread statics QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallException.csIntroduce managed QCall exception status type + custom marshaller.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/InitHelpers.csAdd out QCallException to init-class QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/GenericsHelpers.csAdd out QCallException to generic handle worker QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.csAdd out QCallException to cast helper QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.csAdd out QCallException to async continuation QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.CoreCLR.csAdd out QCallException to unsafe accessor resolver QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.csAdd out QCallException to module name/types QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.csAdd out QCallException to custom attribute QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.csAdd out QCallException to GetCurrentMethod QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.csAdd out QCallException to metadata update QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.csAdd out QCallException to raw metadata QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.csAdd out QCallException to metadata import enum QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/LoaderAllocator.csAdd out QCallException to loader allocator destroy QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeParameterBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.csAdd out QCallException to dynamic assembly creation QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.CoreCLR.csAdd out _ to compile-method QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.csAdd out _ to destroy handle QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/Reflection/AssemblyName.CoreCLR.csAdd out QCallException to assembly spec init QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.csAdd out QCallException to assembly lookup QCalls.
src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.csAdd out _ to clone allocation QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/IO/Stream.CoreCLR.csAdd out QCallException to overridden-check QCall.
src/coreclr/System.Private.CoreLib/src/System/IO/FileLoadException.CoreCLR.csAdd out QCallException to file load message QCalls.
src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.csAdd out QCallException to exception-related QCalls.
src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.csAdd out QCallException to Environment QCalls (Exit/FailFast/GetProcessorCount).
src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.csAdd out QCallException to enum values/names QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreCLR.csAdd out QCallException to stack trace frames QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrame.CoreCLR.csAdd out QCallException to method-desc-from-IP QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Debugger.csAdd out QCallException to debugger break/notification QCalls.
src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.csAdd out QCallException to delegate binding/construct QCalls.
src/coreclr/System.Private.CoreLib/src/System/ComAwareWeakReference.CoreCLR.csAdd out QCallException to COM weak ref QCalls.
src/coreclr/System.Private.CoreLib/src/System/CLRConfig.csAdd out QCallException to CLR config QCall.
src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.csAdd out QCallException to array creation/ctor QCalls.
src/coreclr/System.Private.CoreLib/src/System/ArgIterator.csAdd out QCallException to varargs iterator QCalls.
src/coreclr/System.Private.CoreLib/src/System/AppContext.CoreCLR.csAdd out QCallException to host property QCall.
src/coreclr/System.Private.CoreLib/src/Internal/VersionResilientHashCode.CoreCLR.csAdd out QCallException to version-resilient hash QCall.
src/coreclr/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.csAdd out QCallException to throw-helper QCalls.
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.csAdd conditional out _ to EH QCall internal calls.
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.csAdd conditional out _ to EH QCall internal calls.

Comment threadsrc/coreclr/vm/exceptmacros.h
@azure-pipelines

Copy link
Copy Markdown
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.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
CopilotAI review requested due to automatic review settings August 18, 2026 00:15

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 170 out of 170 changed files in this pull request and generated 1 comment.

CopilotAI review requested due to automatic review settings August 18, 2026 19:45

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 171 out of 171 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallException.cs:26

  • QCallExceptionMarshaller.FromUnmanaged only treats exceptionPending == 1 as an exception. Since the native representation is an INT32 flag, using a non-zero check (!= 0) is more robust against future expansion or accidental non-1 values, and avoids silently skipping exception propagation if the flag is ever set to another non-zero value.

Add managed forwarding overloads for helpers invoked by native IL stub generation so their CoreLib binder signatures remain stable after adding QCall exception parameters.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
Clean up accidental formatting and redundant conditionals introduced by the QCall migration. Make the COM stack trace assertion tolerate non-frame and additional lines while preserving frame-order validation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 31769fe8-86a0-4379-91f3-5353097eb47c
CopilotAI review requested due to automatic review settings September 1, 2026 19:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs:794

  • GetMethod(...) can return null, which would make failures here surface as a NullReferenceException later (or even pass silently if callers change). Since this helper is only for tests, it’s better to assert the MethodInfo is found so failures are clear and localized.
    src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallExceptionStatus.cs:45
  • The <= 2 check is a protocol constant that’s easy to lose track of when the native side evolves. Consider naming it as a constant so the intent is explicit and future updates are harder to miss.
  • Files reviewed: 155/155 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment threadsrc/tests/Interop/COM/NETClients/Primitives/StringTests.cs
Restore normal native QCall return values and pass exception status through a hidden final parameter.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 02:19

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It changes CoreCLR’s QCall exception-handling ABI and dispatch machinery across a wide set of managed and native entrypoints, making the regression surface too large to approve without focused human validation.

Review details
  • Files reviewed: 155/155 changed files
  • Comments generated: 1
  • Review effort level: Lite

@jkotas

jkotas commented Sep 2, 2026

Copy link
Copy Markdown
Member

Restore normal native QCall return values and pass exception status through a hidden final parameter.

Was there a specific problem with HiddenReturnValue that made you switch to HiddenLastParameter?

Comment threadsrc/coreclr/vm/excep.cpp Outdated
Comment threadsrc/coreclr/vm/exceptionhandling.cpp Outdated
Avoid relying on GET_THREAD and explicit MAKE_CURRENT_THREAD_AVAILABLE calls in QCall exception and reflection paths. Retrieve the current thread directly with GetThread, and correct the QCall casing in the exception rethrow helper name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 21:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The generated WASM callhelpers tables appear to drop the CompressionNative_CompressBound entry while managed code still imports it, which risks runtime entrypoint resolution failures on browser/WASI.

Review details

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallExceptionStatus.cs:45

  • QCallExceptionStatusMarshaller treats status values 1 and 2 as special cases, but the code currently uses a hard-coded magic number ("<= 2"). This makes the reserved-status contract harder to maintain and audit.

Consider introducing named constants (e.g., QCallOutOfMemoryExceptionStatus = 1, QCallStackOverflowExceptionStatus = 2, and/or MaxSpecialExceptionStatus = 2) and using them in the comparison.

src/coreclr/vm/wasm/browser/callhelpers-pinvoke.cpp:22

  • CompressionNative_CompressBound was removed from the generated WASM callhelpers P/Invoke table, but managed code still declares a [LibraryImport] for it (Interop.ZLib.compressBound). If this entry is missing at runtime, CompressionNative_CompressBound resolution is likely to fail (EntryPointNotFound / missing symbol) on browser/WASI.

Please ensure the callhelpers generator includes CompressionNative_CompressBound in both browser and WASI outputs (and the table counts match).

  • Files reviewed: 155/155 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Keep the CompressionNative_CompressBound entry in the browser and WASI static P/Invoke tables and update their entry counts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 21:58

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The marshaller’s special-status handling can preserve stale OutOfMemoryException stack traces due to reuse of the preallocated instance, which should be corrected before merging.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 155/155 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment on lines +41 to +61
Exception exception;
if ((nuint)status <= 2)
{
exception = Thread.GetQCallSpecialException(status);
}
else
{
GCHandle exceptionHandle = GCHandle.FromIntPtr(status);
try
{
exception = (Exception)exceptionHandle.Target!;
}
finally
{
exceptionHandle.Free();
}
}

// Throw during unmarshalling so QCall exception propagation remains as close as
// possible to throwing directly from native code, as QCalls did previously.
ExceptionDispatchInfo.Throw(exception);
Comment on lines +60 to +65
internal static Exception GetQCallSpecialException(nint status)
{
Exception? exception = null;
GetQCallSpecialException(status, ObjectHandleOnStack.Create(ref exception));
return exception!;
}
// [DllImport(JitHelpers.QCall, EntryPoint = "FooNative_Bar", CharSet = CharSet.Unicode)]
// // QCalls should always be static extern.
// private static extern bool Bar(int flags, string inString, StringHandleOnStack retString);
// // All QCalls that use BEGIN_QCALL must use the hidden last parameter error handler.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also described in docs\design\coreclr\botr\corelib.md . Can we update that copy as well? (Or deduplicate the description by replacing one of the copies with a link.)

{
"System.StubHelpers.StubHelpers.ThrowInteropParamException",
"Server.Contract.IStringTesting.Reverse_LPWStr_OutAttr",
"Xunit.Assert.RecordException",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is likely to be unreliable with JIT stress when some of the methods can be aggressively inlined and no longer show up in the stacktrace.

@jkotas

Copy link
Copy Markdown
Member

LGTM otherwise

internal static unsafe partial class ThrowHelpers
{
[DoesNotReturn]
[ErrorHandler(typeof(QCallExceptionStatusMarshaller), ErrorLocation.HiddenLastParameter)]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Reposting earlier question since it got lost in the comment noise)

Was there a specific problem with HiddenReturnValue that made you switch to HiddenLastParameter?

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@davidwrighton@jkotas@AustinWise
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Route QCall exceptions through marshalling - #132420

Open
davidwrighton wants to merge 29 commits into
dotnet:mainfrom
davidwrighton:fixup_qcalls
Open

Route QCall exceptions through marshalling#132420
davidwrighton wants to merge 29 commits into
dotnet:mainfrom
davidwrighton:fixup_qcalls

Conversation

@davidwrighton

@davidwrightondavidwrighton commented Aug 17, 2026

Copy link
Copy Markdown
Member

Summary

  • add source-generated LibraryImport support for custom error handlers sourced from the native return value, final parameter, or injected hidden return/parameter locations
  • use the hidden-return error-handler form for CoreCLR QCalls that use BEGIN_QCALL / END_QCALL, while preserving their existing managed signatures
  • change migrated native QCall entry points to return QCallExceptionStatus and move ordinary native return values to explicit out parameters
  • capture exceptions at the QCall boundary and return either a reserved status for preallocated OutOfMemoryException / StackOverflowException instances or a handle to the exact managed exception
  • convert and rethrow the status during managed unmarshalling, preserving the original exception instance and stack trace and releasing exception handles after transfer
  • keep QCalls without the exception-handling macros on their existing ABI, and retain binder-visible helper signatures through managed wrappers where necessary
  • update native declarations and generated browser/WASI call helpers for the new QCall ABI

Testing

  • build.cmd clr+libs -c Checked
  • build.cmd mono+libs
  • build.cmd clr+libs -os browser -c Debug
  • build.cmd clr.corelib -os osx -a x64 -c Debug
  • build.cmd clr.runtime -a x86 -c Checked
  • src\tasks\WasmAppBuilder\generate-coreclr-helpers.cmd -c Debug
  • AmbiguousImplementationException runtime test, including repeated exception propagation
  • RuntimeHelpersTests.QCallExceptionStatus_ThrowsSpecialException
  • RuntimeHelpersTests.QCallExceptionStatus_ThrowsExactExceptionFromHandle
  • COM string marshalling tests covering preservation of the customer-visible exception stack trace
  • migration audit confirming only QCalls using BEGIN_QCALL were converted
  • git diff --check

Note

This pull request description was generated by GitHub Copilot.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @agocke
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR changes CoreCLR QCall exception propagation by routing exceptions thrown inside BEGIN_QCALL/END_QCALL through an out QCallException status value that is custom-marshalled back to managed code. The native side captures throwables onto the managed Thread, and the managed marshaller rethrows during unmarshalling, while leaving non-BEGIN_QCALL QCalls on the prior behavior.

Changes:

  • Introduce QCallException + QCallExceptionMarshaller and update managed QCall signatures to include an out QCallException for BEGIN_QCALL-based QCalls.
  • Update native QCall entrypoints and exception macros to capture exceptions instead of dispatching/rethrowing directly across the boundary.
  • Add the per-thread storage needed to temporarily root the captured throwable and rethrow it from managed unmarshalling.

Reviewed changes

Copilot reviewed 170 out of 170 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
src/libraries/System.Private.CoreLib/src/System/Threading/Wasi/WasiFinalizerScheduler.csAdd conditional out QCallException to WASI finalizer QCalls.
src/libraries/System.Private.CoreLib/src/System/Threading/WaitHandle.Windows.csPass conditional out _ to updated reentrant wait QCall.
src/libraries/System.Private.CoreLib/src/System/Threading/Thread.csUpdate CurrentOSThreadId QCall invocation to include conditional out _.
src/libraries/System.Private.CoreLib/src/System/Threading/Lock.csUse updated Environment.GetProcessorCount signature under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.csThread out QCallException through multiple tracing/ALC QCalls.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/TypeMapLazyDictionary.csAdd out QCallException to QCalls used by type map cache population/lookups.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveC/ObjectiveCMarshal.csPass out _ to updated ObjC marshal QCalls.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.NoCom.csExtend internal signature with conditional out _ under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.csCall updated RegisterIsRootedCallback under CORECLR.
src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.csUpdate tracing QCall invocations to include out _.
src/libraries/System.Private.CoreLib/src/System/Environment.csUpdate ProcessorCount initialization to use CORECLR QCall signature.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/TraceLogging/XplatEventLogger.csAdd out QCallException plumbing to xplat EventSource QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventProvider.csAdd conditional out _ to EventPipeInternal QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.Threads.csAdd conditional out _ to EventPipe session wait/disable QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.csAdd conditional out _ to EventPipe dispatcher QCalls.
src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipe.csAdd conditional out _ to EventPipe enable QCall.
src/libraries/System.Private.CoreLib/src/System/AppContext.csAdd conditional out _ to host property QCall path.
src/coreclr/vm/weakreferencenative.hAdd QCallException* to COM weak ref QCall signatures.
src/coreclr/vm/weakreferencenative.cppAdd QCallException* parameter to COM weak ref QCall implementations.
src/coreclr/vm/wasm/entrypoints.hAdd QCallException* to WASI finalizer exported entrypoints.
src/coreclr/vm/versionresilienthashcode.hAdd QCallException* to version-resilient hash QCall signature.
src/coreclr/vm/versionresilienthashcode.cppAdd QCallException* to version-resilient hash QCall implementation.
src/coreclr/vm/varargsnative.hAdd QCallException* to varargs iterator QCall signatures.
src/coreclr/vm/varargsnative.cppAdd QCallException* to varargs iterator QCall implementations.
src/coreclr/vm/unsafeaccessors.cppAdd QCallException* to unsafe accessor resolver QCall.
src/coreclr/vm/tailcallhelp.hAdd QCallException* to tailcall arg buffer allocation QCall.
src/coreclr/vm/tailcallhelp.cppAdd QCallException* to tailcall arg buffer allocation QCall implementation.
src/coreclr/vm/stubhelpers.hAdd QCallException* to multiple interop/stub helper QCall signatures.
src/coreclr/vm/stackwalk.hAdd QCallException* to AppendExceptionStackFrame QCall signature.
src/coreclr/vm/reflectioninvocation.hAdd QCallException* to reflection invocation QCall signatures.
src/coreclr/vm/qcall.hUpdate QCall guidance/comments and route BEGIN/END macros to capture dispatcher.
src/coreclr/vm/qcall.cppAdd QCallException::SetThrowable implementation and thread rooting logic.
src/coreclr/vm/olevariant.hAdd QCallException* to VARIANT record conversion QCall signature.
src/coreclr/vm/olevariant.cppAdd QCallException* to VARIANT record conversion QCall implementation.
src/coreclr/vm/object.hAdd m_QCallException field + setter on ThreadBaseObject.
src/coreclr/vm/nativelibrarynative.hAdd QCallException* to NativeLibrary load-by-name QCall signature.
src/coreclr/vm/nativelibrarynative.cppAdd QCallException* to NativeLibrary load-by-name QCall implementation.
src/coreclr/vm/nativeeventsource.hAdd QCallException* to EventSource/perf tracing QCall signatures.
src/coreclr/vm/multicorejit.hAdd QCallException* to MultiCoreJIT QCall signatures.
src/coreclr/vm/multicorejit.cppAdd QCallException* to MultiCoreJIT QCall implementations.
src/coreclr/vm/mlinfo.cppAdd QCallException* to StubHelpers_CreateCustomMarshaler QCall implementation.
src/coreclr/vm/method.hppAdd QCallException* to unsafe accessor resolver declaration.
src/coreclr/vm/managedmdimport.hppAdd QCallException* to metadata enum QCall signature.
src/coreclr/vm/managedmdimport.cppAdd QCallException* to metadata enum QCall implementation.
src/coreclr/vm/loaderallocator.hppAdd QCallException* to loader allocator destroy QCall signature.
src/coreclr/vm/loaderallocator.cppAdd QCallException* to loader allocator destroy QCall implementation.
src/coreclr/vm/JitQCallHelpers.hAdd QCallException* to JIT helper QCall signatures.
src/coreclr/vm/jithelpers.cppAdd QCallException* to JIT helper QCalls and adjust handler installation in rare paths.
src/coreclr/vm/interoplibinterface.hAdd QCallException* to ObjC/Java marshal QCall signatures.
src/coreclr/vm/interoplibinterface_objc.cppAdd QCallException* to ObjC marshal QCall implementations.
src/coreclr/vm/interoplibinterface_java.cppAdd QCallException* to Java marshal QCall implementations.
src/coreclr/vm/interoplibinterface_comwrappers.hAdd QCallException* to ComWrappers QCall signatures.
src/coreclr/vm/interoplibinterface_comwrappers.cppAdd QCallException* to ComWrappers QCall implementations.
src/coreclr/vm/ilmarshalers.hAdd QCallException* to safe array marshaler QCall signatures.
src/coreclr/vm/ilmarshalers.cppAdd QCallException* to safe array marshaler QCall implementations.
src/coreclr/vm/finalizerthread.cppAdd QCallException* to WASI finalizer QCall implementations.
src/coreclr/vm/exceptmacros.hIntroduce QCallException native struct and capture-dispatcher exception macros.
src/coreclr/vm/exceptionhandlingqcalls.hAdd QCallException* to exception-handling-related QCall signatures.
src/coreclr/vm/exceptionhandling.cppAdd QCallException* to exception-handling-related QCall implementations.
src/coreclr/vm/excep.cppAdd QCallException* to message QCalls and introduce QCall-specific unwind helper.
src/coreclr/vm/eventpipeinternal.hAdd QCallException* to EventPipeInternal QCall signatures.
src/coreclr/vm/eventpipeinternal.cppAdd QCallException* to EventPipeInternal QCall implementations.
src/coreclr/vm/debugdebugger.hAdd QCallException* to debugger/stacktrace QCall signatures.
src/coreclr/vm/debugdebugger.cppAdd QCallException* to debugger/stacktrace QCall implementations.
src/coreclr/vm/customattribute.hAdd QCallException* to custom attribute QCall signatures.
src/coreclr/vm/customattribute.cppAdd QCallException* to custom attribute QCall implementations.
src/coreclr/vm/corelib.hAdd corelib field mapping for new thread _qcallException field.
src/coreclr/vm/coreassemblyspec.cppAdd QCallException* to assembly spec initialization QCall signature.
src/coreclr/vm/comsynchronizable.hAdd QCallException* to thread/lock-related QCall signatures.
src/coreclr/vm/comsynchronizable.cppAdd QCallException* to thread/lock-related QCall implementations.
src/coreclr/vm/commodule.hAdd QCallException* to Reflection.Emit module builder QCall signatures.
src/coreclr/vm/commodule.cppAdd QCallException* to Reflection.Emit module builder QCall implementations.
src/coreclr/vm/comdependenthandle.hAdd QCallException* to dependent handle QCall signatures.
src/coreclr/vm/comdependenthandle.cppAdd QCallException* to dependent handle QCall implementations.
src/coreclr/vm/comdelegate.hAdd QCallException* to delegate-related QCall signatures.
src/coreclr/vm/comdelegate.cppAdd QCallException* to delegate-related QCall implementations.
src/coreclr/vm/clrconfignative.hAdd QCallException* to CLR config QCall signature.
src/coreclr/vm/clrconfignative.cppAdd QCallException* to CLR config QCall implementation.
src/coreclr/vm/arraynative.hAdd QCallException* to array creation/ctor QCall signatures.
src/coreclr/vm/arraynative.cppAdd QCallException* to array creation/ctor QCall implementations.
src/coreclr/vm/appdomainnative.hppAdd QCallException* to AppDomain/AppContext/string intern QCall signatures.
src/coreclr/vm/appdomainnative.cppAdd QCallException* to AppDomain/AppContext/string intern QCall implementations.
src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csprojInclude new QCallException.cs in CoreCLR CoreLib build.
src/coreclr/System.Private.CoreLib/src/System/Variant.csAdd out QCallException to COM VARIANT-related QCalls.
src/coreclr/System.Private.CoreLib/src/System/ValueType.csAdd out QCallException to ValueType helper QCalls.
src/coreclr/System.Private.CoreLib/src/System/TypeLoadException.CoreCLR.csAdd out QCallException to message-formatting QCall.
src/coreclr/System.Private.CoreLib/src/System/Threading/ObjectHeader.CoreCLR.csAdd out QCallException to lock-object QCall.
src/coreclr/System.Private.CoreLib/src/System/String.CoreCLR.csAdd out QCallException to string intern/is-interned QCalls.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CreateUninitializedCache.CoreCLR.csAdd out QCallException to create-uninitialized info QCall.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.csAdd out QCallException to multiple RuntimeType/reflection QCalls.
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.BoxCache.csAdd out QCallException to boxing cache QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/TrackerObjectManager.CoreCLR.csAdd out QCallException to wrapper-cache registration QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ObjectiveCMarshal.CoreCLR.csAdd out QCallException to ObjC marshal QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeLibrary.CoreCLR.csAdd out QCallException to NativeLibrary load QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/Java/JavaMarshal.CoreCLR.csAdd out QCallException to Java marshal QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/GCHandle.CoreCLR.csAdd out QCallException to GCHandle QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.CoreCLR.csAdd out QCallException to ComWrappers QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/ExceptionServices/InternalCalls.csAdd out QCallException to EH-related QCall internal calls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/DependentHandle.csAdd out QCallException to dependent handle QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/ControlledExecution.CoreCLR.csAdd out QCallException to ThreadNative_Abort QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/VirtualDispatchHelpers.csAdd out QCallException to virtual dispatch resolver QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/StaticsHelpers.csAdd out QCallException to thread statics QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallException.csIntroduce managed QCall exception status type + custom marshaller.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/InitHelpers.csAdd out QCallException to init-class QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/GenericsHelpers.csAdd out QCallException to generic handle worker QCall.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/CastHelpers.csAdd out QCallException to cast helper QCalls.
src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/AsyncHelpers.CoreCLR.csAdd out QCallException to async continuation QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/TypeNameResolver.CoreCLR.csAdd out QCallException to unsafe accessor resolver QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeModule.csAdd out QCallException to module name/types QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.csAdd out QCallException to custom attribute QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBase.CoreCLR.csAdd out QCallException to GetCurrentMethod QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/MetadataUpdater.csAdd out QCallException to metadata update QCalls.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Metadata/AssemblyExtensions.csAdd out QCallException to raw metadata QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/MdImport.csAdd out QCallException to metadata import enum QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/LoaderAllocator.csAdd out QCallException to loader allocator destroy QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimePropertyBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeParameterBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeMethodBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeFieldBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeEventBuilder.csAdd out _ to Reflection.Emit QCall invocations.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/RuntimeAssemblyBuilder.csAdd out QCallException to dynamic assembly creation QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicMethod.CoreCLR.csAdd out _ to compile-method QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.csAdd out _ to destroy handle QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/Reflection/AssemblyName.CoreCLR.csAdd out QCallException to assembly spec init QCall.
src/coreclr/System.Private.CoreLib/src/System/Reflection/Assembly.CoreCLR.csAdd out QCallException to assembly lookup QCalls.
src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.csAdd out _ to clone allocation QCall invocation.
src/coreclr/System.Private.CoreLib/src/System/IO/Stream.CoreCLR.csAdd out QCallException to overridden-check QCall.
src/coreclr/System.Private.CoreLib/src/System/IO/FileLoadException.CoreCLR.csAdd out QCallException to file load message QCalls.
src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.csAdd out QCallException to exception-related QCalls.
src/coreclr/System.Private.CoreLib/src/System/Environment.CoreCLR.csAdd out QCallException to Environment QCalls (Exit/FailFast/GetProcessorCount).
src/coreclr/System.Private.CoreLib/src/System/Enum.CoreCLR.csAdd out QCallException to enum values/names QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackTrace.CoreCLR.csAdd out QCallException to stack trace frames QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/StackFrame.CoreCLR.csAdd out QCallException to method-desc-from-IP QCall.
src/coreclr/System.Private.CoreLib/src/System/Diagnostics/Debugger.csAdd out QCallException to debugger break/notification QCalls.
src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.csAdd out QCallException to delegate binding/construct QCalls.
src/coreclr/System.Private.CoreLib/src/System/ComAwareWeakReference.CoreCLR.csAdd out QCallException to COM weak ref QCalls.
src/coreclr/System.Private.CoreLib/src/System/CLRConfig.csAdd out QCallException to CLR config QCall.
src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.csAdd out QCallException to array creation/ctor QCalls.
src/coreclr/System.Private.CoreLib/src/System/ArgIterator.csAdd out QCallException to varargs iterator QCalls.
src/coreclr/System.Private.CoreLib/src/System/AppContext.CoreCLR.csAdd out QCallException to host property QCall.
src/coreclr/System.Private.CoreLib/src/Internal/VersionResilientHashCode.CoreCLR.csAdd out QCallException to version-resilient hash QCall.
src/coreclr/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.csAdd out QCallException to throw-helper QCalls.
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/StackFrameIterator.csAdd conditional out _ to EH QCall internal calls.
src/coreclr/nativeaot/Runtime.Base/src/System/Runtime/ExceptionHandling.csAdd conditional out _ to EH QCall internal calls.

Comment threadsrc/coreclr/vm/exceptmacros.h
@azure-pipelines

Copy link
Copy Markdown
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.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
CopilotAI review requested due to automatic review settings August 18, 2026 00:15

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 170 out of 170 changed files in this pull request and generated 1 comment.

CopilotAI review requested due to automatic review settings August 18, 2026 19:45

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 171 out of 171 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallException.cs:26

  • QCallExceptionMarshaller.FromUnmanaged only treats exceptionPending == 1 as an exception. Since the native representation is an INT32 flag, using a non-zero check (!= 0) is more robust against future expansion or accidental non-1 values, and avoids silently skipping exception propagation if the flag is ever set to another non-zero value.

Add managed forwarding overloads for helpers invoked by native IL stub generation so their CoreLib binder signatures remain stable after adding QCall exception parameters.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 97d405d-8e39-4e76-afe4-2f94b74ecfa2
Clean up accidental formatting and redundant conditionals introduced by the QCall migration. Make the COM stack trace assertion tolerate non-frame and additional lines while preserving frame-order validation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 31769fe8-86a0-4379-91f3-5353097eb47c
CopilotAI review requested due to automatic review settings September 1, 2026 19:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Runtime/CompilerServices/RuntimeHelpersTests.cs:794

  • GetMethod(...) can return null, which would make failures here surface as a NullReferenceException later (or even pass silently if callers change). Since this helper is only for tests, it’s better to assert the MethodInfo is found so failures are clear and localized.
    src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallExceptionStatus.cs:45
  • The <= 2 check is a protocol constant that’s easy to lose track of when the native side evolves. Consider naming it as a constant so the intent is explicit and future updates are harder to miss.
  • Files reviewed: 155/155 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment threadsrc/tests/Interop/COM/NETClients/Primitives/StringTests.cs
Restore normal native QCall return values and pass exception status through a hidden final parameter.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 02:19

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

It changes CoreCLR’s QCall exception-handling ABI and dispatch machinery across a wide set of managed and native entrypoints, making the regression surface too large to approve without focused human validation.

Review details
  • Files reviewed: 155/155 changed files
  • Comments generated: 1
  • Review effort level: Lite

@jkotas

jkotas commented Sep 2, 2026

Copy link
Copy Markdown
Member

Restore normal native QCall return values and pass exception status through a hidden final parameter.

Was there a specific problem with HiddenReturnValue that made you switch to HiddenLastParameter?

Comment threadsrc/coreclr/vm/excep.cpp Outdated
Comment threadsrc/coreclr/vm/exceptionhandling.cpp Outdated
Avoid relying on GET_THREAD and explicit MAKE_CURRENT_THREAD_AVAILABLE calls in QCall exception and reflection paths. Retrieve the current thread directly with GetThread, and correct the QCall casing in the exception rethrow helper name.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 21:20

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The generated WASM callhelpers tables appear to drop the CompressionNative_CompressBound entry while managed code still imports it, which risks runtime entrypoint resolution failures on browser/WASI.

Review details

Suppressed comments (2)

Previously missed (1) — in code that hasn't changed since the last review.

src/coreclr/System.Private.CoreLib/src/System/Runtime/CompilerServices/QCallExceptionStatus.cs:45

  • QCallExceptionStatusMarshaller treats status values 1 and 2 as special cases, but the code currently uses a hard-coded magic number ("<= 2"). This makes the reserved-status contract harder to maintain and audit.

Consider introducing named constants (e.g., QCallOutOfMemoryExceptionStatus = 1, QCallStackOverflowExceptionStatus = 2, and/or MaxSpecialExceptionStatus = 2) and using them in the comparison.

src/coreclr/vm/wasm/browser/callhelpers-pinvoke.cpp:22

  • CompressionNative_CompressBound was removed from the generated WASM callhelpers P/Invoke table, but managed code still declares a [LibraryImport] for it (Interop.ZLib.compressBound). If this entry is missing at runtime, CompressionNative_CompressBound resolution is likely to fail (EntryPointNotFound / missing symbol) on browser/WASI.

Please ensure the callhelpers generator includes CompressionNative_CompressBound in both browser and WASI outputs (and the table counts match).

  • Files reviewed: 155/155 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Keep the CompressionNative_CompressBound entry in the browser and WASI static P/Invoke tables and update their entry counts.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3bf4a72c-55aa-4b64-8dc3-c0fbeb82730c
CopilotAI review requested due to automatic review settings September 2, 2026 21:58

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The marshaller’s special-status handling can preserve stale OutOfMemoryException stack traces due to reuse of the preallocated instance, which should be corrected before merging.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 155/155 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment on lines +41 to +61
Exception exception;
if ((nuint)status <= 2)
{
exception = Thread.GetQCallSpecialException(status);
}
else
{
GCHandle exceptionHandle = GCHandle.FromIntPtr(status);
try
{
exception = (Exception)exceptionHandle.Target!;
}
finally
{
exceptionHandle.Free();
}
}

// Throw during unmarshalling so QCall exception propagation remains as close as
// possible to throwing directly from native code, as QCalls did previously.
ExceptionDispatchInfo.Throw(exception);
Comment on lines +60 to +65
internal static Exception GetQCallSpecialException(nint status)
{
Exception? exception = null;
GetQCallSpecialException(status, ObjectHandleOnStack.Create(ref exception));
return exception!;
}
// [DllImport(JitHelpers.QCall, EntryPoint = "FooNative_Bar", CharSet = CharSet.Unicode)]
// // QCalls should always be static extern.
// private static extern bool Bar(int flags, string inString, StringHandleOnStack retString);
// // All QCalls that use BEGIN_QCALL must use the hidden last parameter error handler.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also described in docs\design\coreclr\botr\corelib.md . Can we update that copy as well? (Or deduplicate the description by replacing one of the copies with a link.)

{
"System.StubHelpers.StubHelpers.ThrowInteropParamException",
"Server.Contract.IStringTesting.Reverse_LPWStr_OutAttr",
"Xunit.Assert.RecordException",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is likely to be unreliable with JIT stress when some of the methods can be aggressively inlined and no longer show up in the stacktrace.

@jkotas

Copy link
Copy Markdown
Member

LGTM otherwise

internal static unsafe partial class ThrowHelpers
{
[DoesNotReturn]
[ErrorHandler(typeof(QCallExceptionStatusMarshaller), ErrorLocation.HiddenLastParameter)]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Reposting earlier question since it got lost in the comment noise)

Was there a specific problem with HiddenReturnValue that made you switch to HiddenLastParameter?

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@davidwrighton@jkotas@AustinWise