Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
9f19b8a
Convert exception-related MethodDescCallSite calls to UnmanagedCaller…
AaronRobinsonMSFT Feb 24, 2026
bdf1b64
Address review feedback from jkotas
AaronRobinsonMSFT Feb 25, 2026
2dcb570
Fold GetEventArgsForNotification and DeliverExceptionNotification int…
AaronRobinsonMSFT Feb 25, 2026
217a124
Merge remote-tracking branch 'upstream/main' into uco-excep-priority2
AaronRobinsonMSFT Feb 25, 2026
da0c8fb
Refactor exception handling: remove DeliverFirstChanceNotification, a…
AaronRobinsonMSFT Feb 26, 2026
59fe76f
Replace CanDeliverNotificationToCurrentAppDomain with QCall boolean
AaronRobinsonMSFT Feb 26, 2026
496de04
Address review feedback on exception notification refactoring
AaronRobinsonMSFT Feb 26, 2026
b3389e8
Bring back the catch for each handler.
AaronRobinsonMSFT Feb 26, 2026
a0d7185
Copilot feedback.
AaronRobinsonMSFT Feb 26, 2026
1bb0376
Move CallToString to RuntimeHelpers and replace METHOD__OBJECT__TO_ST…
AaronRobinsonMSFT Feb 26, 2026
82fe49c
Feedback.
AaronRobinsonMSFT Feb 26, 2026
5edef69
Remove old comment.
AaronRobinsonMSFT Feb 26, 2026
4f779dc
Update reverse stubs
AaronRobinsonMSFT Feb 26, 2026
357380e
Address PR feedback: move FirstChanceException to AppContext
AaronRobinsonMSFT Feb 27, 2026
54474b8
Refactor OnFirstChanceException method for native AOT compatibility
AaronRobinsonMSFT Feb 27, 2026
147b0ba
Merge branch 'main' into uco-excep-priority2
AaronRobinsonMSFT Feb 27, 2026
0c3fd2b
Merge branch 'main' into uco-excep-priority2
AaronRobinsonMSFT Feb 27, 2026
7428a09
Merge branch 'main' into uco-excep-priority2
AaronRobinsonMSFT Mar 2, 2026
aac1485
Merge branch 'main' into uco-excep-priority2
AaronRobinsonMSFT Mar 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,17 @@ private static unsafe string[] InitializeCommandLineArgs(char* exePath, int argc
[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Environment_GetProcessorCount")]
internal static partial int GetProcessorCount();

// Used by VM
internal static string? GetResourceStringLocal(string key) => SR.GetResourceString(key);
[UnmanagedCallersOnly]
private static unsafe void GetResourceStringLocal(string* pKey, string* pResult, Exception* pException)
Comment thread
AaronRobinsonMSFT marked this conversation as resolved.
Outdated
{
try
{
*pResult = SR.GetResourceString(*pKey);
}
catch (Exception ex)
{
*pException = ex;
}
}
}
}
55 changes: 55 additions & 0 deletions src/coreclr/System.Private.CoreLib/src/System/Exception.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
Comment thread
AaronRobinsonMSFT marked this conversation as resolved.
Outdated
using System.Runtime.InteropServices;
using System.Runtime.Serialization;

Expand Down Expand Up @@ -114,6 +115,19 @@ internal void InternalPreserveStackTrace()
_stackTraceString = null;
}

[UnmanagedCallersOnly]
private static unsafe void InternalPreserveStackTrace(Exception* pException, Exception* pOutException)
{
try
{
pException->InternalPreserveStackTrace();
}
catch (Exception ex)
{
*pOutException = ex;
}
}
Comment thread
AaronRobinsonMSFT marked this conversation as resolved.

[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void PrepareForForeignExceptionRaise();

Expand Down Expand Up @@ -257,6 +271,47 @@ private bool CanSetRemoteStackTrace()
return true;
}

[UnmanagedCallersOnly]
internal static unsafe void CreateRuntimeWrappedException(object* pThrownObject, object* pResult, Exception* pException)
{
try
{
*pResult = new System.Runtime.CompilerServices.RuntimeWrappedException(*pThrownObject);
}
catch (Exception ex)
{
*pException = ex;
}
}

[UnmanagedCallersOnly]
internal static unsafe void CreateTypeInitializationException(char* pTypeName, Exception* pInnerException, object* pResult, Exception* pException)
{
try
{
string? typeName = pTypeName is not null ? new string(pTypeName) : null;
Exception? innerException = pInnerException is not null ? *pInnerException : null;
*pResult = new TypeInitializationException(typeName, innerException);
}
catch (Exception ex)
{
*pException = ex;
}
}

[UnmanagedCallersOnly]
internal static unsafe void CreateFirstChanceExceptionEventArgs(Exception* pInnerException, object* pResult, Exception* pException)
{
try
{
*pResult = new FirstChanceExceptionEventArgs(*pInnerException);
}
catch (Exception ex)
{
*pException = ex;
}
}

#if FEATURE_COMINTEROP
// used by vm
[UnmanagedCallersOnly]
Expand Down
13 changes: 13 additions & 0 deletions src/coreclr/System.Private.CoreLib/src/System/Object.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,19 @@ namespace System
{
public partial class Object
{
[UnmanagedCallersOnly]
private static unsafe void GetToString(object* pObj, string* pResult, Exception* pException)
Comment thread
AaronRobinsonMSFT marked this conversation as resolved.
Outdated
{
try
{
*pResult = pObj->ToString();
}
catch (Exception ex)
{
*pException = ex;
}
}

// Returns a Type object which represent this object instance.
[Intrinsic]
public unsafe Type GetType()
Expand Down
11 changes: 6 additions & 5 deletions src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ DEFINE_CLASS(RUNE, Text, Rune)
DEFINE_CLASS(ENUM, System, Enum)

DEFINE_CLASS(ENVIRONMENT, System, Environment)
DEFINE_METHOD(ENVIRONMENT, GET_RESOURCE_STRING_LOCAL, GetResourceStringLocal, SM_Str_RetStr)
DEFINE_METHOD(ENVIRONMENT, GET_RESOURCE_STRING_LOCAL, GetResourceStringLocal, SM_PtrStr_PtrStr_PtrException_RetVoid)
DEFINE_METHOD(ENVIRONMENT, INITIALIZE_COMMAND_LINE_ARGS, InitializeCommandLineArgs, SM_PtrChar_Int_PtrPtrChar_RetArrStr)

DEFINE_CLASS(EVENT, Reflection, RuntimeEventInfo)
Expand All @@ -300,7 +300,10 @@ DEFINE_FIELD_U(_xptrs, ExceptionObject, _xptrs)
DEFINE_FIELD_U(_xcode, ExceptionObject, _xcode)
DEFINE_FIELD_U(_HResult, ExceptionObject, _HResult)
DEFINE_CLASS(EXCEPTION, System, Exception)
DEFINE_METHOD(EXCEPTION, INTERNAL_PRESERVE_STACK_TRACE, InternalPreserveStackTrace, IM_RetVoid)
DEFINE_METHOD(EXCEPTION, INTERNAL_PRESERVE_STACK_TRACE, InternalPreserveStackTrace, SM_PtrException_PtrException_RetVoid)
DEFINE_METHOD(EXCEPTION, CREATE_RUNTIME_WRAPPED_EXCEPTION, CreateRuntimeWrappedException, SM_PtrObj_PtrObj_PtrException_RetVoid)
DEFINE_METHOD(EXCEPTION, CREATE_TYPE_INIT_EXCEPTION, CreateTypeInitializationException, SM_PtrChar_PtrException_PtrObj_PtrException_RetVoid)
DEFINE_METHOD(EXCEPTION, CREATE_FIRSTCHANCE_EVENTARGS, CreateFirstChanceExceptionEventArgs, SM_PtrException_PtrObj_PtrException_RetVoid)
#ifdef FEATURE_COMINTEROP
DEFINE_METHOD(EXCEPTION, GET_DESCRIPTION_BSTR, GetDescriptionBstr, SM_PtrException_PtrException_RetIntPtr)
DEFINE_METHOD(EXCEPTION, GET_SOURCE_BSTR, GetSourceBstr, SM_PtrException_PtrException_RetIntPtr)
Expand All @@ -313,7 +316,6 @@ DEFINE_METHOD(SYSTEM_EXCEPTION, STR_EX_CTOR, .ctor,


DEFINE_CLASS(TYPE_INIT_EXCEPTION, System, TypeInitializationException)
DEFINE_METHOD(TYPE_INIT_EXCEPTION, STR_EX_CTOR, .ctor, IM_Str_Exception_RetVoid)

DEFINE_CLASS(VALUETASK_1, Tasks, ValueTask`1)
DEFINE_METHOD(VALUETASK_1, GET_ISCOMPLETED, get_IsCompleted, NoSig)
Expand Down Expand Up @@ -603,6 +605,7 @@ DEFINE_METHOD(READONLY_SPAN, GET_ITEM, get_Item, IM_Int_Ret
DEFINE_METHOD(OBJECT, CTOR, .ctor, IM_RetVoid)
DEFINE_METHOD(OBJECT, FINALIZE, Finalize, IM_RetVoid)
DEFINE_METHOD(OBJECT, TO_STRING, ToString, IM_RetStr)
DEFINE_METHOD(OBJECT, GET_TO_STRING, GetToString, SM_PtrObj_PtrStr_PtrException_RetVoid)
DEFINE_METHOD(OBJECT, GET_TYPE, GetType, IM_RetType)
DEFINE_METHOD(OBJECT, GET_HASH_CODE, GetHashCode, IM_RetInt)
DEFINE_METHOD(OBJECT, EQUALS, Equals, IM_Obj_RetBool)
Expand Down Expand Up @@ -853,7 +856,6 @@ DEFINE_FIELD(CONTINUATION, STATE, State)
DEFINE_FIELD(CONTINUATION, FLAGS, Flags)

DEFINE_CLASS(RUNTIME_WRAPPED_EXCEPTION, CompilerServices, RuntimeWrappedException)
DEFINE_METHOD(RUNTIME_WRAPPED_EXCEPTION, OBJ_CTOR, .ctor, IM_Obj_RetVoid)
DEFINE_FIELD(RUNTIME_WRAPPED_EXCEPTION, WRAPPED_EXCEPTION, _wrappedException)

DEFINE_CLASS(CALLCONV_CDECL, CompilerServices, CallConvCdecl)
Expand Down Expand Up @@ -984,7 +986,6 @@ DEFINE_METHOD(TYPE, GET_TYPE_FROM_HANDLE, GetTypeFromHandle,
DEFINE_CLASS(TYPE_DELEGATOR, Reflection, TypeDelegator)

DEFINE_CLASS(FIRSTCHANCE_EVENTARGS, ExceptionServices, FirstChanceExceptionEventArgs)
Comment thread
AaronRobinsonMSFT marked this conversation as resolved.
Outdated
DEFINE_METHOD(FIRSTCHANCE_EVENTARGS, CTOR, .ctor, IM_Exception_RetVoid)

DEFINE_CLASS(EXCEPTION_DISPATCH_INFO, ExceptionServices, ExceptionDispatchInfo)
DEFINE_METHOD(EXCEPTION_DISPATCH_INFO, CAPTURE, Capture, NoSig)
Expand Down
122 changes: 36 additions & 86 deletions src/coreclr/vm/excep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,21 +237,22 @@ STRINGREF GetExceptionMessage(OBJECTREF throwable)
if (throwable == NULL)
return NULL;

// Return value.
STRINGREF pString = NULL;

GCPROTECT_BEGIN(throwable);
struct
{
OBJECTREF throwable;
STRINGREF pString;
} gc;
gc.throwable = throwable;
gc.pString = NULL;
GCPROTECT_BEGIN(gc);

// Call Object.ToString(). Note that exceptions do not have to inherit from System.Exception
MethodDescCallSite toString(METHOD__OBJECT__TO_STRING, &throwable);

// Make the call.
ARG_SLOT arg[1] = {ObjToArgSlot(throwable)};
pString = toString.Call_RetSTRINGREF(arg);
UnmanagedCallersOnlyCaller getToString(METHOD__OBJECT__GET_TO_STRING);
getToString.InvokeThrowing(&gc.throwable, &gc.pString);

GCPROTECT_END();

return pString;
return gc.pString;
}

HRESULT GetExceptionHResult(OBJECTREF throwable)
Expand Down Expand Up @@ -385,12 +386,9 @@ void ExceptionPreserveStackTrace( // No return.
{
LOG((LF_EH, LL_INFO1000, "ExceptionPreserveStackTrace called\n"));

// Call Exception.InternalPreserveStackTrace() ...
MethodDescCallSite preserveStackTrace(METHOD__EXCEPTION__INTERNAL_PRESERVE_STACK_TRACE, &throwable);

// Make the call.
ARG_SLOT arg[1] = {ObjToArgSlot(throwable)};
preserveStackTrace.Call(arg);
// Call Exception.InternalPreserveStackTrace()
UnmanagedCallersOnlyCaller preserveStackTrace(METHOD__EXCEPTION__INTERNAL_PRESERVE_STACK_TRACE);
preserveStackTrace.InvokeThrowing(&throwable);
}

GCPROTECT_END();
Expand Down Expand Up @@ -433,19 +431,12 @@ void WrapNonCompliantException(OBJECTREF *ppThrowable)
if (pFD_WrappedException == NULL)
pFD_WrappedException = CoreLibBinder::GetField(FIELD__RUNTIME_WRAPPED_EXCEPTION__WRAPPED_EXCEPTION);

OBJECTREF orWrapper = AllocateObject(CoreLibBinder::GetException(kRuntimeWrappedException));
OBJECTREF orWrapper = NULL;

GCPROTECT_BEGIN(orWrapper);

MethodDescCallSite ctor(METHOD__RUNTIME_WRAPPED_EXCEPTION__OBJ_CTOR, &orWrapper);

ARG_SLOT args[] =
{
ObjToArgSlot(orWrapper),
ObjToArgSlot(*ppThrowable)
};

ctor.Call(args);
UnmanagedCallersOnlyCaller createWrapper(METHOD__EXCEPTION__CREATE_RUNTIME_WRAPPED_EXCEPTION);
createWrapper.InvokeThrowing(ppThrowable, &orWrapper);

*ppThrowable = orWrapper;

Expand Down Expand Up @@ -535,10 +526,6 @@ void CreateTypeInitializationExceptionObject(LPCWSTR pTypeThatFailed,
isAlreadyCreating(pThread->IsCreatingTypeInitException());

EX_TRY {
// This will contain the type of exception we want to create. Read comment below
// on why we'd want to create an exception other than TypeInitException
MethodTable *pMT;
BinderMethodID methodID;

// If we are already in the midst of creating a TypeInitializationException object,
// and we get here, it means there was an exception thrown while initializing the
Expand All @@ -549,8 +536,6 @@ void CreateTypeInitializationExceptionObject(LPCWSTR pTypeThatFailed,
// in the code that follows.
Comment thread
AaronRobinsonMSFT marked this conversation as resolved.
Outdated
if (!isAlreadyCreating.GetValue()) {
Comment thread
AaronRobinsonMSFT marked this conversation as resolved.
Outdated
pThread->SetIsCreatingTypeInitException();
pMT = CoreLibBinder::GetException(kTypeInitializationException);
methodID = METHOD__TYPE_INIT_EXCEPTION__STR_EX_CTOR;
}
else {
// If we ever hit one of these asserts, then it is bad
Expand All @@ -562,28 +547,22 @@ void CreateTypeInitializationExceptionObject(LPCWSTR pTypeThatFailed,
goto ErrExit;
}

// Allocate the exception object
*pThrowable = AllocateObject(pMT);

MethodDescCallSite ctor(methodID, pThrowable);

// Since the inner exception object in the .ctor is of type Exception, make sure
// that the object we're passed in derives from Exception. If not, pass NULL.
BOOL isException = FALSE;
if (pInnerException != NULL)
isException = IsException((*pInnerException)->GetMethodTable());
{
Comment thread
AaronRobinsonMSFT marked this conversation as resolved.
Outdated
BOOL isException = FALSE;
if (pInnerException != NULL)
isException = IsException((*pInnerException)->GetMethodTable());

_ASSERTE(isException); // What pathway can give us non-compliant exceptions?
OBJECTREF innerEx = isException ? *pInnerException : NULL;

STRINGREF sType = StringObject::NewString(pTypeThatFailed);
GCPROTECT_BEGIN(innerEx);

// If the inner object derives from exception, set it as the third argument.
ARG_SLOT args[] = { ObjToArgSlot(*pThrowable),
ObjToArgSlot(sType),
ObjToArgSlot(isException ? *pInnerException : NULL) };
UnmanagedCallersOnlyCaller createTypeInitEx(METHOD__EXCEPTION__CREATE_TYPE_INIT_EXCEPTION);
createTypeInitEx.InvokeThrowing(pTypeThatFailed, &innerEx, pThrowable);

// Call the .ctor
ctor.Call(args);
GCPROTECT_END();
}

// On success, set the init exception.
*pInitException = *pThrowable;
Expand Down Expand Up @@ -2196,23 +2175,18 @@ STRINGREF GetResourceStringFromManaged(STRINGREF key)
}
CONTRACTL_END;
Comment thread
AaronRobinsonMSFT marked this conversation as resolved.
Outdated

struct xx {
struct
{
STRINGREF key;
STRINGREF ret;
} gc;

gc.key = key;
gc.ret = NULL;

GCPROTECT_BEGIN(gc);

MethodDescCallSite getResourceStringLocal(METHOD__ENVIRONMENT__GET_RESOURCE_STRING_LOCAL);

// Call Environment::GetResourceStringLocal(String name). Returns String value (or maybe null)
// Don't need to GCPROTECT pArgs, since it's not used after the function call.

ARG_SLOT pArgs[1] = { ObjToArgSlot(gc.key) };
gc.ret = getResourceStringLocal.Call_RetSTRINGREF(pArgs);
UnmanagedCallersOnlyCaller getResourceStringLocal(METHOD__ENVIRONMENT__GET_RESOURCE_STRING_LOCAL);
getResourceStringLocal.InvokeThrowing(&gc.key, &gc.ret);

GCPROTECT_END();

Expand Down Expand Up @@ -9597,45 +9571,21 @@ void ExceptionNotifications::GetEventArgsForNotification(ExceptionNotificationHa
}
CONTRACTL_END;
Comment thread
AaronRobinsonMSFT marked this conversation as resolved.
Outdated

MethodTable *pMTEventArgs = NULL;
BinderMethodID idEventArgsCtor = METHOD__FIRSTCHANCE_EVENTARGS__CTOR;

EX_TRY
{
switch(notificationType)
{
case FirstChanceExceptionHandler:
pMTEventArgs = CoreLibBinder::GetClass(CLASS__FIRSTCHANCE_EVENTARGS);
idEventArgsCtor = METHOD__FIRSTCHANCE_EVENTARGS__CTOR;
{
// FirstChance notification takes only a single argument: the exception object.
UnmanagedCallersOnlyCaller createEventArgs(METHOD__EXCEPTION__CREATE_FIRSTCHANCE_EVENTARGS);
createEventArgs.InvokeThrowing(pThrowable, pOutEventArgs);
break;
}
default:
_ASSERTE(!"Invalid Exception Notification Handler!");
break;
}

// Allocate the instance of the eventargs corresponding to the notification
*pOutEventArgs = AllocateObject(pMTEventArgs);

// Prepare to invoke the .ctor
MethodDescCallSite ctor(idEventArgsCtor, pOutEventArgs);

// Setup the arguments to be passed to the notification specific EventArgs .ctor
if (notificationType == FirstChanceExceptionHandler)
{
// FirstChance notification takes only a single argument: the exception object.
ARG_SLOT args[] =
{
ObjToArgSlot(*pOutEventArgs),
ObjToArgSlot(*pThrowable),
};

ctor.Call(args);
}
else
{
// Since we have already asserted above, just set the args to NULL.
*pOutEventArgs = NULL;
}
}
EX_CATCH
{
Expand Down
6 changes: 6 additions & 0 deletions src/coreclr/vm/metasig.h
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,12 @@ DEFINE_METASIG_T(SM(PtrResolver_PtrInt_PtrClass_PtrException_RetVoid, P(C(RESOLV
DEFINE_METASIG_T(SM(PtrResolver_PtrInt_PtrInt_PtrInt_PtrArrByte_PtrException_RetVoid, P(C(RESOLVER)) P(i) P(i) P(i) P(a(b)) P(C(EXCEPTION)), v))
DEFINE_METASIG_T(SM(PtrResolver_PtrArrByte_PtrException_RetVoid, P(C(RESOLVER)) P(a(b)) P(C(EXCEPTION)), v))
DEFINE_METASIG_T(SM(PtrResolver_Int_PtrStr_PtrException_RetVoid, P(C(RESOLVER)) i P(s) P(C(EXCEPTION)), v))
DEFINE_METASIG_T(SM(PtrObj_PtrObj_PtrException_RetVoid, P(j) P(j) P(C(EXCEPTION)), v))
DEFINE_METASIG_T(SM(PtrObj_PtrStr_PtrException_RetVoid, P(j) P(s) P(C(EXCEPTION)), v))
DEFINE_METASIG_T(SM(PtrStr_PtrStr_PtrException_RetVoid, P(s) P(s) P(C(EXCEPTION)), v))
DEFINE_METASIG_T(SM(PtrException_PtrException_RetVoid, P(C(EXCEPTION)) P(C(EXCEPTION)), v))
DEFINE_METASIG_T(SM(PtrChar_PtrException_PtrObj_PtrException_RetVoid, P(u) P(C(EXCEPTION)) P(j) P(C(EXCEPTION)), v))
DEFINE_METASIG_T(SM(PtrException_PtrObj_PtrException_RetVoid, P(C(EXCEPTION)) P(j) P(C(EXCEPTION)), v))

// fields - e.g.:
// DEFINE_METASIG(Fld(PtrVoid, P(v)))
Expand Down
Loading