Replace `CallDescrWorker` with `UnmanagedCallersOnlyAttribute` pattern
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
This issue tracks the work to replace `MethodDescCallSite` / `CallDescrWorker` infrastructure with more efficient `UnmanagedCallersOnly` reverse P/Invoke calls for invoking managed code from native code.
### Benefits
- Significantly reduced overhead for VM-to-managed calls
- Uses existing reverse P/Invoke infrastructure instead of the expensive `CallDescrWorker` mechanism
- Better alignment with modern interop patterns
### Pattern
The conversion pattern involves:
1. Adding `[UnmanagedCallersOnly]` wrappers to managed methods with an `Exception*` out-parameter
2. Using the `UnmanagedCallersOnlyCaller` template class in C++ to invoke these methods
3. Updating `metasig.h` and `corelib.h` with appropriate signatures
#### Notes
- The `UnmanagedCallersOnlyCaller` template class is defined in `callhelpers.h` and can be used to invoke the target.
- WASM builds require regenerating `callhelpers-reverse.cpp` when adding new UCO methods.
Example PR to follow: https://github.com/dotnet/runtime/pull/123832
---
## Conversion Tasks
### Priority 1: Cross-Platform, Simple Conversions
These are straightforward conversions on code paths that run on all platforms.
#### AppDomain / Assembly Loading (`appdomain.cpp`) - https://github.com/dotnet/runtime/pull/123967
- [x] `METHOD__ASSEMBLYLOADCONTEXT__ON_ASSEMBLY_LOAD` in `AppDomain::RaiseAssemblyLoadEvent`
- [x] `METHOD__ASSEMBLYLOADCONTEXT__ON_TYPE_RESOLVE` in `AppDomain::RaiseTypeResolveEventThrowing`
- [x] `METHOD__ASSEMBLYLOADCONTEXT__ON_RESOURCE_RESOLVE` in `AppDomain::RaiseResourceResolveEvent`
- [x] `METHOD__ASSEMBLYLOADCONTEXT__ON_ASSEMBLY_RESOLVE` in `AppDomain::RaiseAssemblyResolveEvent`
- [x] `METHOD__ASSEMBLYLOADCONTEXT__RESOLVE` in `AppDomain::BindAssemblySpec`
- [x] `METHOD__ASSEMBLYLOADCONTEXT__RESOLVESATELLITEASSEMBLY` in `AppDomain::BindSatelliteResourceByResourceRoots`
- [x] `METHOD__ASSEMBLYLOADCONTEXT__RESOLVEUSINGEVENT` in `AppDomain::RaiseLoadFileEvent`
#### Loader Allocator (`loaderallocator.cpp`) - https://github.com/dotnet/runtime/pull/124303
- [x] `METHOD__LOADERALLOCATOR__CTOR` in `LoaderAllocator::Init`
#### Custom Marshaler (`custommarshalerinfo.cpp`) - https://github.com/dotnet/runtime/pull/124440
- [x] `GetCustomMarshaler` method lookup in `CustomMarshalerInfo::CustomMarshalerInfo`
#### Dynamic Methods / Resolver (`dynamicmethod.cpp`) - https://github.com/dotnet/runtime/pull/124303
- [x] `METHOD__RESOLVER__GET_JIT_CONTEXT` in `LCGMethodResolver::GetJitContext`
- [x] `METHOD__RESOLVER__GET_CODE_INFO` in `LCGMethodResolver::GetCodeInfo`
- [x] `METHOD__RESOLVER__GET_LOCALS_SIGNATURE` in `LCGMethodResolver::GetLocalSig`
- [x] `METHOD__RESOLVER__GET_STRING_LITERAL` in `LCGMethodResolver::GetStringLiteral`
#### Interop Utilities (`interoputil.cpp`) - https://github.com/dotnet/runtime/pull/124303
- [x] `METHOD__CULTURE_INFO__INT_CTOR` in `GetCultureInfoForLCID`
- [x] `METHOD__COLORMARSHALER__CONVERT_TO_MANAGED` in `ConvertOleColorToSystemColor`
- [x] `METHOD__COLORMARSHALER__CONVERT_TO_NATIVE` in `ConvertSystemColorToOleColor`
#### Threads / Culture (`threads.cpp`) - https://github.com/dotnet/runtime/pull/124303
- [x] `METHOD__CULTURE_INFO__GET_CURRENT_CULTURE` / `METHOD__CULTURE_INFO__GET_CURRENT_UI_CULTURE` in `Thread::GetCultureInfo`
- [x] `METHOD__CULTURE_INFO__SET_CURRENT_CULTURE` / `METHOD__CULTURE_INFO__SET_CURRENT_UI_CULTURE` in `Thread::SetCultureInfo`
#### Event Sources (`corhost.cpp`) - https://github.com/dotnet/runtime/pull/124303
- [x] `METHOD__EVENT_SOURCE__INITIALIZE_DEFAULT_EVENT_SOURCES` in `CorHost2::CreateAppDomain`
#### Diagnostics (`ds-rt-coreclr.h`) - https://github.com/dotnet/runtime/pull/124303
- [x] `METHOD__STARTUP_HOOK_PROVIDER__CALL_STARTUP_HOOK` in `ds_rt_apply_startup_hook`
---
### Priority 2: Cross-Platform, Exception Handling
#### Exception Construction (`clrex.cpp`) - https://github.com/dotnet/runtime/pull/126061
- [x] Exception constructor in `EEException::CreateThrowable`
- [x] Exception constructor in `EEMessageException::CreateThrowable`
- [x] Exception constructor in `EEResourceException::CreateThrowable`
#### Exception Construction (`excep.cpp`) - https://github.com/dotnet/runtime/pull/124834
- [x] `METHOD__RUNTIME_WRAPPED_EXCEPTION__OBJ_CTOR` in `WrapThrowableInRuntimeWrappedException`
- [x] Various exception constructors in `CreateTypeInitializationExceptionObject`
#### Exception Utilities (`excep.cpp`) - https://github.com/dotnet/runtime/pull/124834
- [x] `METHOD__OBJECT__TO_STRING` in `GetExceptionMessage`
- [x] `METHOD__EXCEPTION__INTERNAL_PRESERVE_STACK_TRACE` in `RaiseTheExceptionInternalOnly`
- [x] `METHOD__ENVIRONMENT__GET_RESOURCE_STRING_LOCAL` in `GetResourceStringFromManaged`
- [x] Event args constructor in `ExceptionNotifications::DeliverExceptionNotification`
#### Exception Info Retrieval (`comutilnative.cpp`) - https://github.com/dotnet/runtime/pull/123986
- [x] `METHOD__EXCEPTION__GET_MESSAGE` in `ExceptionNative::GetMessageFromException`
- [x] `METHOD__EXCEPTION__GET_CLASS_NAME` in `ExceptionNative::GetMessageFromException`
- [x] `METHOD__EXCEPTION__GET_SOURCE` in `ExceptionNative::GetSource`
- [x] `METHOD__EXCEPTION__GET_HELP_CONTEXT` in `ExceptionNative::GetHelpContext`
---
### Priority 3: Cross-Platform, Entry Points (Complex)
These are entry points or thread-related and require careful consideration.
#### Application Entry Points (`assembly.cpp`)
- [x] Thread start / Main entry point in `RunMainInternal` - https://github.com/dotnet/runtime/pull/126222
- [x] `METHOD__STARTUP_HOOK_PROVIDER__MANAGED_STARTUP` in `Assembly::ExecuteMainMethod` - https://github.com/dotnet/runtime/pull/124854
#### Process Lifecycle (`appdomain.cpp`) - https://github.com/dotnet/runtime/pull/124854
- [x] `METHOD__APPCONTEXT__ON_UNHANDLED_EXCEPTION` in `AppDomain::RaiseUnhandledExceptionEvent`
- [x] `METHOD__APPCONTEXT__ON_PROCESS_EXIT` in `AppDomain::RaiseExitProcessEvent`
#### Invoke / Reflection (`invokeutil.cpp`) - https://github.com/dotnet/runtime/pull/124854
- [x] Constructor invocation in `InvokeUtil::CreateObject`
- [x] Constructor invocation in `InvokeUtil::CreateValueType`
#### Host Callbacks (`corhost.cpp`) - https://github.com/dotnet/runtime/pull/126222
- [x] Method invocation in `CorHost2::ExecuteAssembly`
### Exception handling
- [x] ThrowHwEx` in HandleManagedFault and HandleHardwareException
- [x] `ThrowEx` in DispatchManagedException
- [x] `Rethrow` in `DispatchRethrownManagedException`
#### Runtime threads
- [x] `FinalizerThread::FinalizeAllObjects`
- [x] Thread.Start - `KickOffThread_Worker`
#### Reflection Invoke and similar
- [ ] MethodInfo.Invoke - `RuntimeMethodHandle_InvokeMethod`
- [ ] Custom attribute constructor in `COMCustomAttribute::CreateCaObject`
- [ ] `FuncEvalWrapper()` and `DoNormalFuncEval()`
---
### Priority 4: Platform-Specific (ObjC - macOS/iOS)
#### ObjC Interop (`interoplibinterface_objc.cpp`) - https://github.com/dotnet/runtime/pull/124446
- [x] `METHOD__OBJCMARSHAL__AVAILABLEUNHANDLEDEXCEPTIONPROPAGATION` in `ObjCMarshalNative::GetPropagatingExceptionCallback`
---
### Priority 5: Windows-Only (COM Interop)
These files are only compiled on Windows and are lowest priority.
#### COM Connection Points (`comconnectionpoints.cpp`) - https://github.com/dotnet/runtime/issues/123864
- [x] Delegate constructor in `ConnectionPoint::Advise`
- [x] Provider method in `ConnectionPoint::Advise`
#### Standard Interfaces (`stdinterfaces.cpp`) - https://github.com/dotnet/runtime/issues/123864
- [x] `CanRead` property getter in `IDispatchExInfo::GetIDsOfNames`
- [x] `CanWrite` property getter in `IDispatchExInfo::GetIDsOfNames`
#### CLR to COM Calls (`clrtocomcall.cpp`)
- [x] `METHOD__COM_OBJECT__GET_EVENT_PROVIDER` in `ComPlusMethodFrame::DoSlowPathComPlusCall` - https://github.com/dotnet/runtime/pull/125326
- [x] `METHOD__CLASS__FORWARD_CALL_TO_INVOKE` in `CLRToCOMLateBoundWorker` - https://github.com/dotnet/runtime/pull/125326
- [x] Event implmentation dispatch in `CLRToCOMEventCallWorker`
#### COM Callable Wrapper (`comcallablewrapper.cpp`) - https://github.com/dotnet/runtime/issues/123864
- [x] Custom `ICustomQueryInterface::GetInterface` in `ComCallWrapper::CallICustomQueryInterface`
#### Custom Marshaler (`custommarshalerinfo.cpp`) - https://github.com/dotnet/runtime/pull/125326
- [x] `METHOD__STUBHELPERS__GET_IENUMERATOR_TO_ENUM_VARIANT_MARSHALER` in `CustomMarshalerInfo::GetIEnumeratorToEnumVariantMarshaler`
#### Runtime Callable Wrapper (`runtimecallablewrapper.cpp`)
- [x] `METHOD__LICENSE_INTEROP_PROXY__CREATE` in `LicenseInteropHelper::GetCurrentContextInfo` - https://github.com/dotnet/runtime/pull/126141
- [x] `METHOD__LICENSE_INTEROP_PROXY__GETCURRENTCONTEXTINFO` in `LicenseInteropHelper::GetCurrentContextInfo` - https://github.com/dotnet/runtime/pull/126141
- [x] `METHOD__LICENSE_INTEROP_PROXY__SAVEKEYINCURRENTCONTEXT` in `LicenseInteropHelper::SaveKeyInCurrentContext` - https://github.com/dotnet/runtime/pull/126141
#### OLE Variant (`olevariant.cpp`) - https://github.com/dotnet/runtime/pull/125326
- [x] `METHOD__VARIANT__CAST_VARIANT` in `OleVariant::MarshalOleVariantForObject`
- [x] `METHOD__VARIANT__CONVERT_VARIANT_TO_OBJECT` in `OleVariant::MarshalObjectForOleVariant`
- [x] `METHOD__VARIANT__CONVERT_OBJECT_TO_VARIANT` in `OleVariant::MarshalOleVariantForObject`
#### Dispatch Info (`dispatchinfo.cpp`) - ~30 call sites - https://github.com/dotnet/runtime/pull/126074
Various reflection-based calls in:
- [x] `DispatchMemberInfo::GetParamInfo` - parameter name retrieval
- [x] `DispatchMemberInfo::SetUpMethodMarshalerInfo` - method handle retrieval
- [x] `DispatchMemberInfo::GetMemberInfoValue` - property getters
- [x] `DispatchMemberInfo::SetMemberInfoValue` - property setters
- [x] `DispatchExInfo::InvokeMember` - member invocation
- [x] `DispatchExInfo::SynchWithManagedView` - `GetProperties`, `GetFields`, `GetMethods`
- [x] `DispatchInfo::GetExceptionDescription` - exception property getter
---
### Follow up cleanup
- [ ] Delete CallDescrWorker
Contributor guide
Assessment
This issue has not been assessed yet.