From 5b3126e430e3319950482fdb3f2b7781316c028d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Sun, 3 Mar 2024 02:44:22 +0100 Subject: [PATCH 01/77] Make delegates immutable --- .../src/System/Delegate.CoreCLR.cs | 112 ++++++++++-------- .../src/System/MulticastDelegate.CoreCLR.cs | 37 +++--- src/coreclr/vm/amd64/asmconstants.h | 10 +- src/coreclr/vm/arm/asmconstants.h | 2 +- src/coreclr/vm/arm64/asmconstants.h | 2 +- src/coreclr/vm/comdelegate.cpp | 4 +- src/coreclr/vm/corelib.h | 2 +- src/coreclr/vm/i386/asmconstants.h | 8 +- src/coreclr/vm/loongarch64/asmconstants.h | 2 +- src/coreclr/vm/object.cpp | 11 ++ src/coreclr/vm/object.h | 5 +- src/coreclr/vm/riscv64/asmconstants.h | 2 +- 12 files changed, 111 insertions(+), 86 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 1168784b30563b..cee476ce61ae86 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -14,13 +14,14 @@ namespace System [ComVisible(true)] public abstract partial class Delegate : ICloneable, ISerializable { + // Caches MethodInfos, added either after first request or assigned from a DynamicMethod + // For open delegates to collectible types, this may contain a LoaderAllocator object + // that prevents the type from being unloaded. + internal static readonly ConditionalWeakTable s_methodCache = new(); + // _target is the object we will invoke on internal object? _target; // Initialized by VM as needed; null if static delegate - // MethodBase, either cached after first request or assigned from a DynamicMethod - // For open delegates to collectible types, this may be a LoaderAllocator object - internal object? _methodBase; // Initialized by VM as needed - // _methodPtr is a pointer to the method we will invoke // It could be a small thunk if this is a static or UM call internal IntPtr _methodPtr; @@ -130,10 +131,10 @@ public override bool Equals([NotNullWhen(true)] object? obj) // method ptrs don't match, go down long path // - if (_methodBase == null || d._methodBase == null || !(_methodBase is MethodInfo) || !(d._methodBase is MethodInfo)) - return InternalEqualMethodHandles(this, d); - else - return _methodBase.Equals(d._methodBase); + if (s_methodCache.TryGetValue(this, out object? thisCache) && thisCache is MethodInfo thisMethod && + s_methodCache.TryGetValue(d, out object? otherCache) && otherCache is MethodInfo otherMethod) + return thisMethod.Equals(otherMethod); + return InternalEqualMethodHandles(this, d); } public override int GetHashCode() @@ -156,57 +157,70 @@ public override int GetHashCode() protected virtual MethodInfo GetMethodImpl() { - if ((_methodBase == null) || !(_methodBase is MethodInfo)) + if (s_methodCache.TryGetValue(this, out object? cachedValue) && cachedValue is MethodInfo methodInfo) + { + return methodInfo; + } + + return GetMethodImplUncached(); + } + + private MethodInfo GetMethodImplUncached() + { + IRuntimeMethodInfo method = FindMethodHandle(); + RuntimeType? declaringType = RuntimeMethodHandle.GetDeclaringType(method); + // need a proper declaring type instance method on a generic type + if (declaringType.IsGenericType) { - IRuntimeMethodInfo method = FindMethodHandle(); - RuntimeType? declaringType = RuntimeMethodHandle.GetDeclaringType(method); - // need a proper declaring type instance method on a generic type - if (declaringType.IsGenericType) + bool isStatic = (RuntimeMethodHandle.GetAttributes(method) & MethodAttributes.Static) != (MethodAttributes)0; + if (!isStatic) { - bool isStatic = (RuntimeMethodHandle.GetAttributes(method) & MethodAttributes.Static) != (MethodAttributes)0; - if (!isStatic) + if (_methodPtrAux == IntPtr.Zero) { - if (_methodPtrAux == IntPtr.Zero) + // The target may be of a derived type that doesn't have visibility onto the + // target method. We don't want to call RuntimeType.GetMethodBase below with that + // or reflection can end up generating a MethodInfo where the ReflectedType cannot + // see the MethodInfo itself and that breaks an important invariant. But the + // target type could include important generic type information we need in order + // to work out what the exact instantiation of the method's declaring type is. So + // we'll walk up the inheritance chain (which will yield exactly instantiated + // types at each step) until we find the declaring type. Since the declaring type + // we get from the method is probably shared and those in the hierarchy we're + // walking won't be we compare using the generic type definition forms instead. + Type? currentType = _target!.GetType(); + Type targetType = declaringType.GetGenericTypeDefinition(); + while (currentType != null) { - // The target may be of a derived type that doesn't have visibility onto the - // target method. We don't want to call RuntimeType.GetMethodBase below with that - // or reflection can end up generating a MethodInfo where the ReflectedType cannot - // see the MethodInfo itself and that breaks an important invariant. But the - // target type could include important generic type information we need in order - // to work out what the exact instantiation of the method's declaring type is. So - // we'll walk up the inheritance chain (which will yield exactly instantiated - // types at each step) until we find the declaring type. Since the declaring type - // we get from the method is probably shared and those in the hierarchy we're - // walking won't be we compare using the generic type definition forms instead. - Type? currentType = _target!.GetType(); - Type targetType = declaringType.GetGenericTypeDefinition(); - while (currentType != null) + if (currentType.IsGenericType && + currentType.GetGenericTypeDefinition() == targetType) { - if (currentType.IsGenericType && - currentType.GetGenericTypeDefinition() == targetType) - { - declaringType = currentType as RuntimeType; - break; - } - currentType = currentType.BaseType; + declaringType = currentType as RuntimeType; + break; } - - // RCWs don't need to be "strongly-typed" in which case we don't find a base type - // that matches the declaring type of the method. This is fine because interop needs - // to work with exact methods anyway so declaringType is never shared at this point. - Debug.Assert(currentType != null || _target.GetType().IsCOMObject, "The class hierarchy should declare the method"); - } - else - { - // it's an open one, need to fetch the first arg of the instantiation - MethodInfo invoke = this.GetType().GetMethod("Invoke")!; - declaringType = (RuntimeType)invoke.GetParametersAsSpan()[0].ParameterType; + currentType = currentType.BaseType; } + + // RCWs don't need to be "strongly-typed" in which case we don't find a base type + // that matches the declaring type of the method. This is fine because interop needs + // to work with exact methods anyway so declaringType is never shared at this point. + Debug.Assert(currentType != null || _target.GetType().IsCOMObject, "The class hierarchy should declare the method"); + } + else + { + // it's an open one, need to fetch the first arg of the instantiation + MethodInfo invoke = GetType().GetMethod("Invoke")!; + declaringType = (RuntimeType)invoke.GetParametersAsSpan()[0].ParameterType; } } - _methodBase = (MethodInfo)RuntimeType.GetMethodBase(declaringType, method)!; } - return (MethodInfo)_methodBase; + MethodInfo methodInfo = (MethodInfo)RuntimeType.GetMethodBase(declaringType, method)!; + SetCachedMethod(methodInfo); + return methodInfo; + } + + internal void SetCachedMethod(object? value) + { + s_methodCache.AddOrUpdate(this, value); } public object? Target => GetTarget(); diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index ab63d4cf83b74d..fd85dbdf64614b 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -200,10 +200,10 @@ internal void StoreDynamicMethod(MethodInfo dynamicMethod) Debug.Assert(!IsUnmanagedFunctionPtr(), "dynamic method and unmanaged fntptr delegate combined"); // must be a secure/wrapper one, unwrap and save MulticastDelegate d = ((MulticastDelegate?)_invocationList)!; - d._methodBase = dynamicMethod; + d.SetCachedMethod(dynamicMethod); } else - _methodBase = dynamicMethod; + SetCachedMethod(dynamicMethod); } // This method will combine this delegate with the passed delegate @@ -515,21 +515,24 @@ protected override MethodInfo GetMethodImpl() { // we handle unmanaged function pointers here because the generic ones (used for WinRT) would otherwise // be treated as open delegates by the base implementation, resulting in failure to get the MethodInfo - if ((_methodBase == null) || !(_methodBase is MethodInfo)) + if (s_methodCache.TryGetValue(this, out object? cachedValue) && cachedValue is MethodInfo methodInfo) { - IRuntimeMethodInfo method = FindMethodHandle(); - RuntimeType declaringType = RuntimeMethodHandle.GetDeclaringType(method); + return methodInfo; + } - // need a proper declaring type instance method on a generic type - if (declaringType.IsGenericType) - { - // we are returning the 'Invoke' method of this delegate so use this.GetType() for the exact type - RuntimeType reflectedType = (RuntimeType)GetType(); - declaringType = reflectedType; - } - _methodBase = (MethodInfo)RuntimeType.GetMethodBase(declaringType, method)!; + IRuntimeMethodInfo method = FindMethodHandle(); + RuntimeType declaringType = RuntimeMethodHandle.GetDeclaringType(method); + + // need a proper declaring type instance method on a generic type + if (declaringType.IsGenericType) + { + // we are returning the 'Invoke' method of this delegate so use this.GetType() for the exact type + RuntimeType reflectedType = (RuntimeType)GetType(); + declaringType = reflectedType; } - return (MethodInfo)_methodBase; + methodInfo = (MethodInfo)RuntimeType.GetMethodBase(declaringType, method)!; + SetCachedMethod(methodInfo); + return methodInfo; } // Otherwise, must be an inner delegate of a wrapper delegate of an open virtual method. In that case, call base implementation @@ -595,7 +598,7 @@ private void CtorCollectibleClosedStatic(object target, IntPtr methodPtr, IntPtr { this._target = target; this._methodPtr = methodPtr; - this._methodBase = GCHandle.InternalGet(gchandle); + this.SetCachedMethod(GCHandle.InternalGet(gchandle)); } [DebuggerNonUserCode] @@ -605,7 +608,7 @@ private void CtorCollectibleOpened(object target, IntPtr methodPtr, IntPtr shuff this._target = this; this._methodPtr = shuffleThunk; this._methodPtrAux = methodPtr; - this._methodBase = GCHandle.InternalGet(gchandle); + this.SetCachedMethod(GCHandle.InternalGet(gchandle)); } [DebuggerNonUserCode] @@ -614,7 +617,7 @@ private void CtorCollectibleVirtualDispatch(object target, IntPtr methodPtr, Int { this._target = this; this._methodPtr = shuffleThunk; - this._methodBase = GCHandle.InternalGet(gchandle); + this.SetCachedMethod(GCHandle.InternalGet(gchandle)); this.InitializeVirtualCallStub(methodPtr); } #pragma warning restore IDE0060 diff --git a/src/coreclr/vm/amd64/asmconstants.h b/src/coreclr/vm/amd64/asmconstants.h index e12f3e1eafd26e..277fe6b23816b4 100644 --- a/src/coreclr/vm/amd64/asmconstants.h +++ b/src/coreclr/vm/amd64/asmconstants.h @@ -138,7 +138,7 @@ ASMCONSTANTS_C_ASSERT(OFFSETOF__ThreadExceptionState__m_pCurrentTracker -#define OFFSETOF__DelegateObject___methodPtr 0x18 +#define OFFSETOF__DelegateObject___methodPtr 0x10 ASMCONSTANT_OFFSETOF_ASSERT(DelegateObject, _methodPtr); #define OFFSETOF__DelegateObject___target 0x08 @@ -680,13 +680,13 @@ template class FindCompileTimeConstant { private: - FindCompileTimeConstant(); + FindCompileTimeConstant(); }; void BogusFunction() { - // Sample usage to generate the error - FindCompileTimeConstant bogus_variable; - FindCompileTimeConstant bogus_variable2; + // Sample usage to generate the error + FindCompileTimeConstant bogus_variable; + FindCompileTimeConstant bogus_variable2; } #endif // defined(__cplusplus) && defined(USE_COMPILE_TIME_CONSTANT_FINDER) diff --git a/src/coreclr/vm/arm/asmconstants.h b/src/coreclr/vm/arm/asmconstants.h index 16931168e3ce0d..76b583c8e2893a 100644 --- a/src/coreclr/vm/arm/asmconstants.h +++ b/src/coreclr/vm/arm/asmconstants.h @@ -64,7 +64,7 @@ ASMCONSTANTS_C_ASSERT(LazyMachState_captureSp == offsetof(LazyMachState, capture #define LazyMachState_captureIp (LazyMachState_captureSp+4) ASMCONSTANTS_C_ASSERT(LazyMachState_captureIp == offsetof(LazyMachState, captureIp)) -#define DelegateObject___methodPtr 0x0c +#define DelegateObject___methodPtr 0x08 ASMCONSTANTS_C_ASSERT(DelegateObject___methodPtr == offsetof(DelegateObject, _methodPtr)); #define DelegateObject___target 0x04 diff --git a/src/coreclr/vm/arm64/asmconstants.h b/src/coreclr/vm/arm64/asmconstants.h index cc19728d9e29ee..5b67217d47d4a8 100644 --- a/src/coreclr/vm/arm64/asmconstants.h +++ b/src/coreclr/vm/arm64/asmconstants.h @@ -109,7 +109,7 @@ ASMCONSTANTS_C_ASSERT(LazyMachState_captureIp == offsetof(LazyMachState, capture #define VASigCookie__pNDirectILStub 0x8 ASMCONSTANTS_C_ASSERT(VASigCookie__pNDirectILStub == offsetof(VASigCookie, pNDirectILStub)) -#define DelegateObject___methodPtr 0x18 +#define DelegateObject___methodPtr 0x10 ASMCONSTANTS_C_ASSERT(DelegateObject___methodPtr == offsetof(DelegateObject, _methodPtr)); #define DelegateObject___target 0x08 diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index ef4021039a66b8..6597486ca8e161 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -2808,8 +2808,8 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT LoaderAllocator *pTargetMethodLoaderAllocator = pTargetMethod->GetLoaderAllocator(); BOOL isCollectible = pTargetMethodLoaderAllocator->IsCollectible(); // A method that may be instantiated over a collectible type, and is static will require a delegate - // that has the _methodBase field filled in with the LoaderAllocator of the collectible assembly - // associated with the instantiation. + // that has the LoaderAllocator of the collectible assembly associated with the instantiation + // stored in the MethodInfo cache. BOOL fMaybeCollectibleAndStatic = FALSE; // Do not allow static methods with [UnmanagedCallersOnlyAttribute] to be a delegate target. diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index c52c58954165a2..12b5b6b1d1ac2c 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -249,7 +249,6 @@ DEFINE_METHOD(DECIMAL, CURRENCY_CTOR, .ctor, DEFINE_CLASS_U(System, Delegate, NoClass) DEFINE_FIELD_U(_target, DelegateObject, _target) -DEFINE_FIELD_U(_methodBase, DelegateObject, _methodBase) DEFINE_FIELD_U(_methodPtr, DelegateObject, _methodPtr) DEFINE_FIELD_U(_methodPtrAux, DelegateObject, _methodPtrAux) DEFINE_CLASS(DELEGATE, System, Delegate) @@ -258,6 +257,7 @@ DEFINE_FIELD(DELEGATE, METHOD_PTR, _methodPtr) DEFINE_FIELD(DELEGATE, METHOD_PTR_AUX, _methodPtrAux) DEFINE_METHOD(DELEGATE, CONSTRUCT_DELEGATE, DelegateConstruct, IM_Obj_IntPtr_RetVoid) DEFINE_METHOD(DELEGATE, GET_INVOKE_METHOD, GetInvokeMethod, IM_RetIntPtr) +DEFINE_METHOD(DELEGATE, SET_CACHED_METHOD, SetCachedMethod, IM_Obj_RetVoid) DEFINE_CLASS(INT128, System, Int128) DEFINE_CLASS(UINT128, System, UInt128) diff --git a/src/coreclr/vm/i386/asmconstants.h b/src/coreclr/vm/i386/asmconstants.h index 7de14a6c063184..2437efcb72d58b 100644 --- a/src/coreclr/vm/i386/asmconstants.h +++ b/src/coreclr/vm/i386/asmconstants.h @@ -301,14 +301,12 @@ ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_pCalleeSavedFP == offsetof(InlinedCall #ifdef FEATURE_STUBS_AS_IL // DelegateObject from src/vm/object.h #define DelegateObject___target 0x04 // offset 0 is m_pMethTab of base class Object -#define DelegateObject___methodBase 0x08 -#define DelegateObject___methodPtr 0x0c -#define DelegateObject___methodPtrAux 0x10 +#define DelegateObject___methodPtr 0x08 +#define DelegateObject___methodPtrAux 0x0c #define DelegateObject___invocationList 0x14 -#define DelegateObject___invocationCount 0x18 +#define DelegateObject___invocationCount 0x14 ASMCONSTANTS_C_ASSERT(DelegateObject___target == offsetof(DelegateObject, _target)); -ASMCONSTANTS_C_ASSERT(DelegateObject___methodBase == offsetof(DelegateObject, _methodBase)); ASMCONSTANTS_C_ASSERT(DelegateObject___methodPtr == offsetof(DelegateObject, _methodPtr)); ASMCONSTANTS_C_ASSERT(DelegateObject___methodPtrAux == offsetof(DelegateObject, _methodPtrAux)); ASMCONSTANTS_C_ASSERT(DelegateObject___invocationList == offsetof(DelegateObject, _invocationList)); diff --git a/src/coreclr/vm/loongarch64/asmconstants.h b/src/coreclr/vm/loongarch64/asmconstants.h index e12d0040a74d0a..2d47ceea2f2675 100644 --- a/src/coreclr/vm/loongarch64/asmconstants.h +++ b/src/coreclr/vm/loongarch64/asmconstants.h @@ -134,7 +134,7 @@ ASMCONSTANTS_C_ASSERT(LazyMachState_captureIp == offsetof(LazyMachState, capture #define VASigCookie__pNDirectILStub 0x8 ASMCONSTANTS_C_ASSERT(VASigCookie__pNDirectILStub == offsetof(VASigCookie, pNDirectILStub)) -#define DelegateObject___methodPtr 0x18 +#define DelegateObject___methodPtr 0x10 ASMCONSTANTS_C_ASSERT(DelegateObject___methodPtr == offsetof(DelegateObject, _methodPtr)); #define DelegateObject___target 0x08 diff --git a/src/coreclr/vm/object.cpp b/src/coreclr/vm/object.cpp index 213a9cde5925b3..be2d13c1e79254 100644 --- a/src/coreclr/vm/object.cpp +++ b/src/coreclr/vm/object.cpp @@ -1394,6 +1394,17 @@ void __fastcall ZeroMemoryInGCHeap(void* mem, size_t size) *memBytes++ = 0; } +void DelegateObject::SetMethodBase(OBJECTREF newMethodBase) +{ + STANDARD_VM_CONTRACT; + GCX_COOP(); + PREPARE_NONVIRTUAL_CALLSITE(METHOD__DELEGATE__SET_CACHED_METHOD); + DECLARE_ARGHOLDER_ARRAY(args, 2); + args[ARGNUM_0] = PTR_TO_ARGHOLDER(this); + args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(newMethodBase); + CALL_MANAGED_METHOD_NORET(args); +} + void StackTraceArray::Append(StackTraceElement const * begin, StackTraceElement const * end) { CONTRACTL diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index 915e45deca0636..5617cf84e9a015 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -2000,7 +2000,7 @@ class DelegateObject : public Object void SetInvocationCount(INT_PTR invocationCount) { LIMITED_METHOD_CONTRACT; _invocationCount = invocationCount; } static int GetOffsetOfInvocationCount() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _invocationCount); } - void SetMethodBase(OBJECTREF newMethodBase) { LIMITED_METHOD_CONTRACT; SetObjectReference((OBJECTREF*)&_methodBase, newMethodBase); } + void SetMethodBase(OBJECTREF newMethodBase); // README: // If you modify the order of these fields, make sure to update the definition in @@ -2008,7 +2008,6 @@ class DelegateObject : public Object private: // System.Delegate OBJECTREF _target; - OBJECTREF _methodBase; PCODE _methodPtr; PCODE _methodPtrAux; // System.MulticastDelegate @@ -2017,7 +2016,7 @@ class DelegateObject : public Object }; #define OFFSETOF__DelegateObject__target OBJECT_SIZE /* m_pMethTab */ -#define OFFSETOF__DelegateObject__methodPtr (OFFSETOF__DelegateObject__target + TARGET_POINTER_SIZE /* _target */ + TARGET_POINTER_SIZE /* _methodBase */) +#define OFFSETOF__DelegateObject__methodPtr (OFFSETOF__DelegateObject__target + TARGET_POINTER_SIZE /* _target */) #define OFFSETOF__DelegateObject__methodPtrAux (OFFSETOF__DelegateObject__methodPtr + TARGET_POINTER_SIZE /* _methodPtr */) #ifdef USE_CHECKED_OBJECTREFS diff --git a/src/coreclr/vm/riscv64/asmconstants.h b/src/coreclr/vm/riscv64/asmconstants.h index 71095e3cffc994..57f9a63fbbd172 100644 --- a/src/coreclr/vm/riscv64/asmconstants.h +++ b/src/coreclr/vm/riscv64/asmconstants.h @@ -129,7 +129,7 @@ ASMCONSTANTS_C_ASSERT(LazyMachState_captureIp == offsetof(LazyMachState, capture #define VASigCookie__pNDirectILStub 0x8 ASMCONSTANTS_C_ASSERT(VASigCookie__pNDirectILStub == offsetof(VASigCookie, pNDirectILStub)) -#define DelegateObject___methodPtr 0x18 +#define DelegateObject___methodPtr 0x10 ASMCONSTANTS_C_ASSERT(DelegateObject___methodPtr == offsetof(DelegateObject, _methodPtr)); #define DelegateObject___target 0x08 From 64baa1d37fb26ad0e216ef7a9fb6f1bf17892cfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Sun, 3 Mar 2024 02:53:35 +0100 Subject: [PATCH 02/77] Add a comment --- .../System.Private.CoreLib/src/System/Delegate.CoreCLR.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index cee476ce61ae86..f7179f7005394f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -218,6 +218,7 @@ private MethodInfo GetMethodImplUncached() return methodInfo; } + // this is called by the VM in addition to calls from managed code internal void SetCachedMethod(object? value) { s_methodCache.AddOrUpdate(this, value); From a489288ec2e78e4969a65216e27799f706fc0c3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Sun, 3 Mar 2024 03:00:23 +0100 Subject: [PATCH 03/77] Optimize cache checks --- .../src/System/Delegate.CoreCLR.cs | 2 +- .../src/System/MulticastDelegate.CoreCLR.cs | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index f7179f7005394f..e6472a947336ee 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -165,7 +165,7 @@ protected virtual MethodInfo GetMethodImpl() return GetMethodImplUncached(); } - private MethodInfo GetMethodImplUncached() + internal MethodInfo GetMethodImplUncached() { IRuntimeMethodInfo method = FindMethodHandle(); RuntimeType? declaringType = RuntimeMethodHandle.GetDeclaringType(method); diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index fd85dbdf64614b..caec944abe8a46 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -495,6 +495,16 @@ public sealed override int GetHashCode() } protected override MethodInfo GetMethodImpl() + { + if (s_methodCache.TryGetValue(this, out object? cachedValue) && cachedValue is MethodInfo methodInfo) + { + return methodInfo; + } + + return GetMulticastMethodImplUncached(); + } + + internal MethodInfo GetMulticastMethodImplUncached() { if (_invocationCount != 0 && _invocationList != null) { @@ -515,10 +525,6 @@ protected override MethodInfo GetMethodImpl() { // we handle unmanaged function pointers here because the generic ones (used for WinRT) would otherwise // be treated as open delegates by the base implementation, resulting in failure to get the MethodInfo - if (s_methodCache.TryGetValue(this, out object? cachedValue) && cachedValue is MethodInfo methodInfo) - { - return methodInfo; - } IRuntimeMethodInfo method = FindMethodHandle(); RuntimeType declaringType = RuntimeMethodHandle.GetDeclaringType(method); @@ -530,13 +536,13 @@ protected override MethodInfo GetMethodImpl() RuntimeType reflectedType = (RuntimeType)GetType(); declaringType = reflectedType; } - methodInfo = (MethodInfo)RuntimeType.GetMethodBase(declaringType, method)!; + MethodInfo methodInfo = (MethodInfo)RuntimeType.GetMethodBase(declaringType, method)!; SetCachedMethod(methodInfo); return methodInfo; } // Otherwise, must be an inner delegate of a wrapper delegate of an open virtual method. In that case, call base implementation - return base.GetMethodImpl(); + return GetMethodImplUncached(); } // this should help inlining From bc132e438b38b28e99146a5c1a9369788ce18026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Sun, 3 Mar 2024 23:07:46 +0100 Subject: [PATCH 04/77] Fix x86 --- .../src/System/Delegate.CoreCLR.cs | 21 +++++++++++-------- .../src/System/MulticastDelegate.CoreCLR.cs | 2 +- src/coreclr/vm/i386/asmconstants.h | 2 +- .../src/System/Delegate.cs | 6 +++--- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index e6472a947336ee..da6b41b3306da9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -14,11 +14,6 @@ namespace System [ComVisible(true)] public abstract partial class Delegate : ICloneable, ISerializable { - // Caches MethodInfos, added either after first request or assigned from a DynamicMethod - // For open delegates to collectible types, this may contain a LoaderAllocator object - // that prevents the type from being unloaded. - internal static readonly ConditionalWeakTable s_methodCache = new(); - // _target is the object we will invoke on internal object? _target; // Initialized by VM as needed; null if static delegate @@ -131,8 +126,8 @@ public override bool Equals([NotNullWhen(true)] object? obj) // method ptrs don't match, go down long path // - if (s_methodCache.TryGetValue(this, out object? thisCache) && thisCache is MethodInfo thisMethod && - s_methodCache.TryGetValue(d, out object? otherCache) && otherCache is MethodInfo otherMethod) + if (Cache.s_methodCache.TryGetValue(this, out object? thisCache) && thisCache is MethodInfo thisMethod && + Cache.s_methodCache.TryGetValue(d, out object? otherCache) && otherCache is MethodInfo otherMethod) return thisMethod.Equals(otherMethod); return InternalEqualMethodHandles(this, d); } @@ -157,7 +152,7 @@ public override int GetHashCode() protected virtual MethodInfo GetMethodImpl() { - if (s_methodCache.TryGetValue(this, out object? cachedValue) && cachedValue is MethodInfo methodInfo) + if (Cache.s_methodCache.TryGetValue(this, out object? cachedValue) && cachedValue is MethodInfo methodInfo) { return methodInfo; } @@ -221,7 +216,7 @@ internal MethodInfo GetMethodImplUncached() // this is called by the VM in addition to calls from managed code internal void SetCachedMethod(object? value) { - s_methodCache.AddOrUpdate(this, value); + Cache.s_methodCache.AddOrUpdate(this, value); } public object? Target => GetTarget(); @@ -534,6 +529,14 @@ internal void InitializeVirtualCallStub(IntPtr methodPtr) { return (_methodPtrAux == IntPtr.Zero) ? _target : null; } + + // Caches MethodInfos, added either after first request or assigned from a DynamicMethod + // For open delegates to collectible types, this may contain a LoaderAllocator object + // that prevents the type from being unloaded. + internal static class Cache + { + public static readonly ConditionalWeakTable s_methodCache = new(); + } } // These flags effect the way BindToMethodInfo and BindToMethodName are allowed to bind a delegate to a target method. Their diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index caec944abe8a46..c84d383b3e0c22 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -496,7 +496,7 @@ public sealed override int GetHashCode() protected override MethodInfo GetMethodImpl() { - if (s_methodCache.TryGetValue(this, out object? cachedValue) && cachedValue is MethodInfo methodInfo) + if (Cache.s_methodCache.TryGetValue(this, out object? cachedValue) && cachedValue is MethodInfo methodInfo) { return methodInfo; } diff --git a/src/coreclr/vm/i386/asmconstants.h b/src/coreclr/vm/i386/asmconstants.h index 2437efcb72d58b..1ab9406b8ec6c9 100644 --- a/src/coreclr/vm/i386/asmconstants.h +++ b/src/coreclr/vm/i386/asmconstants.h @@ -303,7 +303,7 @@ ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_pCalleeSavedFP == offsetof(InlinedCall #define DelegateObject___target 0x04 // offset 0 is m_pMethTab of base class Object #define DelegateObject___methodPtr 0x08 #define DelegateObject___methodPtrAux 0x0c -#define DelegateObject___invocationList 0x14 +#define DelegateObject___invocationList 0x10 #define DelegateObject___invocationCount 0x14 ASMCONSTANTS_C_ASSERT(DelegateObject___target == offsetof(DelegateObject, _target)); diff --git a/src/libraries/System.Private.CoreLib/src/System/Delegate.cs b/src/libraries/System.Private.CoreLib/src/System/Delegate.cs index d24059770d5f88..6660c3976df00f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Delegate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Delegate.cs @@ -74,8 +74,8 @@ public abstract partial class Delegate : ICloneable, ISerializable /// The order of the delegates returned by the enumerator is the same order in which the current delegate invokes the methods that those delegates represent. /// The method returns an empty enumerator for null delegate. /// - public static System.Delegate.InvocationListEnumerator EnumerateInvocationList(TDelegate? d) where TDelegate : System.Delegate - => new InvocationListEnumerator(Unsafe.As(d)); + public static Delegate.InvocationListEnumerator EnumerateInvocationList(TDelegate? d) where TDelegate : System.Delegate + => new Delegate.InvocationListEnumerator(Unsafe.As(d)); /// /// Provides an enumerator for the invocation list of a delegate. @@ -119,7 +119,7 @@ public bool MoveNext() /// Implement IEnumerable.GetEnumerator() to return 'this' as the IEnumerator /// [EditorBrowsable(EditorBrowsableState.Never)] // Only here to make foreach work - public System.Delegate.InvocationListEnumerator GetEnumerator() => this; + public Delegate.InvocationListEnumerator GetEnumerator() => this; } public object? DynamicInvoke(params object?[]? args) From 2dce6a845646ff98a790e61762e2b26961b5cdbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Mon, 4 Mar 2024 01:06:58 +0100 Subject: [PATCH 05/77] Fix R2R --- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index ad83b1eb42a5d6..4cd6f5abae7b2a 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -456,7 +456,7 @@ unsafe partial class CorInfoImpl { private const CORINFO_RUNTIME_ABI TargetABI = CORINFO_RUNTIME_ABI.CORINFO_CORECLR_ABI; - private uint OffsetOfDelegateFirstTarget => (uint)(3 * PointerSize); // Delegate::m_functionPointer + private uint OffsetOfDelegateFirstTarget => (uint)(2 * PointerSize); // Delegate::m_functionPointer private readonly ReadyToRunCodegenCompilation _compilation; private MethodWithGCInfo _methodCodeNode; @@ -2114,7 +2114,7 @@ private void ceeInfoGetCallInfo( // of shared generic code calling a shared generic implementation method, which should be rare. // // An alternative design would be to add a new generic dictionary entry kind to hold the MethodDesc - // of the constrained target instead, and use that in some circumstances; however, implementation of + // of the constrained target instead, and use that in some circumstances; however, implementation of // that design requires refactoring variuos parts of the JIT interface as well as // TryResolveConstraintMethodApprox. In particular we would need to be abled to embed a constrained lookup // via EmbedGenericHandle, as well as decide in TryResolveConstraintMethodApprox if the call can be made From 00fcd02e187515bbcec2b7c74adb4f4842169d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 4 Mar 2024 16:30:58 +0100 Subject: [PATCH 06/77] Update methodcontext.cpp --- src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index c72b8b1eec1f6c..efd869681442fa 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -4370,7 +4370,7 @@ void MethodContext::repGetEEInfo(CORINFO_EE_INFO* pEEInfoOut) pEEInfoOut->offsetOfThreadFrame = (unsigned)0x10; pEEInfoOut->offsetOfGCState = (unsigned)0xc; pEEInfoOut->offsetOfDelegateInstance = (unsigned)0x8; - pEEInfoOut->offsetOfDelegateFirstTarget = (unsigned)0x18; + pEEInfoOut->offsetOfDelegateFirstTarget = (unsigned)0x10; pEEInfoOut->offsetOfWrapperDelegateIndirectCell = (unsigned)0x40; pEEInfoOut->sizeOfReversePInvokeFrame = (unsigned)0x8; pEEInfoOut->osPageSize = (size_t)0x1000; From 7941123c0d8d6121e7ec8f777b127f76382ba8ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 4 Mar 2024 16:37:03 +0100 Subject: [PATCH 07/77] Fix contract --- src/coreclr/vm/object.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/object.cpp b/src/coreclr/vm/object.cpp index be2d13c1e79254..4963c6ea3b1304 100644 --- a/src/coreclr/vm/object.cpp +++ b/src/coreclr/vm/object.cpp @@ -1396,7 +1396,14 @@ void __fastcall ZeroMemoryInGCHeap(void* mem, size_t size) void DelegateObject::SetMethodBase(OBJECTREF newMethodBase) { - STANDARD_VM_CONTRACT; + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + GCX_COOP(); PREPARE_NONVIRTUAL_CALLSITE(METHOD__DELEGATE__SET_CACHED_METHOD); DECLARE_ARGHOLDER_ARRAY(args, 2); From 4de27544979df7655149b7202ce1f1f906447154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 4 Mar 2024 16:40:11 +0100 Subject: [PATCH 08/77] Update src/coreclr/vm/object.cpp Co-authored-by: Jan Kotas --- src/coreclr/vm/object.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/vm/object.cpp b/src/coreclr/vm/object.cpp index 4963c6ea3b1304..a5c7cbc82bac27 100644 --- a/src/coreclr/vm/object.cpp +++ b/src/coreclr/vm/object.cpp @@ -1400,11 +1400,10 @@ void DelegateObject::SetMethodBase(OBJECTREF newMethodBase) { NOTHROW; GC_TRIGGERS; - MODE_ANY; + MODE_COOPERATIVE; } CONTRACTL_END; - GCX_COOP(); PREPARE_NONVIRTUAL_CALLSITE(METHOD__DELEGATE__SET_CACHED_METHOD); DECLARE_ARGHOLDER_ARRAY(args, 2); args[ARGNUM_0] = PTR_TO_ARGHOLDER(this); From b9f7f14000fb2307eaa6eb373ce4f584a4b9b475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Mon, 4 Mar 2024 17:01:21 +0100 Subject: [PATCH 09/77] Swap fields --- .../src/System/Delegate.CoreCLR.cs | 8 ++++---- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 2 +- .../superpmi/superpmi-shared/methodcontext.cpp | 8 ++++---- src/coreclr/vm/amd64/asmconstants.h | 2 +- src/coreclr/vm/arm/asmconstants.h | 2 +- src/coreclr/vm/arm64/asmconstants.h | 2 +- src/coreclr/vm/corelib.h | 14 +++++++------- src/coreclr/vm/i386/asmconstants.h | 4 ++-- src/coreclr/vm/loongarch64/asmconstants.h | 2 +- src/coreclr/vm/object.h | 14 +++++++------- src/coreclr/vm/riscv64/asmconstants.h | 2 +- 11 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index da6b41b3306da9..76912df5ff93b8 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -17,16 +17,16 @@ public abstract partial class Delegate : ICloneable, ISerializable // _target is the object we will invoke on internal object? _target; // Initialized by VM as needed; null if static delegate - // _methodPtr is a pointer to the method we will invoke - // It could be a small thunk if this is a static or UM call - internal IntPtr _methodPtr; - // In the case of a static method passed to a delegate, this field stores // whatever _methodPtr would have stored: and _methodPtr points to a // small thunk which removes the "this" pointer before going on // to _methodPtrAux. internal IntPtr _methodPtrAux; + // _methodPtr is a pointer to the method we will invoke + // It could be a small thunk if this is a static or UM call + internal IntPtr _methodPtr; + // This constructor is called from the class generated by the // compiler generated code [RequiresUnreferencedCode("The target method might be removed")] diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 4cd6f5abae7b2a..f78401f14c39f2 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -456,7 +456,7 @@ unsafe partial class CorInfoImpl { private const CORINFO_RUNTIME_ABI TargetABI = CORINFO_RUNTIME_ABI.CORINFO_CORECLR_ABI; - private uint OffsetOfDelegateFirstTarget => (uint)(2 * PointerSize); // Delegate::m_functionPointer + private uint OffsetOfDelegateFirstTarget => (uint)(3 * PointerSize); // Delegate::m_functionPointer private readonly ReadyToRunCodegenCompilation _compilation; private MethodWithGCInfo _methodCodeNode; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index efd869681442fa..900f4690199f3a 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -3631,10 +3631,10 @@ void MethodContext::recGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOC void MethodContext::dmpGetThreadLocalStaticBlocksInfo(DWORD key, const Agnostic_GetThreadLocalStaticBlocksInfo& value) { printf("GetThreadLocalStaticBlocksInfo key %u, tlsIndex-%s, " - ", tlsGetAddrFtnPtr-%016" PRIX64 ", tlsIndexObject - %016" PRIX64 + ", tlsGetAddrFtnPtr-%016" PRIX64 ", tlsIndexObject - %016" PRIX64 ", threadVarsSection - %016" PRIX64 ", offsetOfThreadLocalStoragePointer-%u, offsetOfMaxThreadStaticBlocks-%u" - ", offsetOfThreadStaticBlocks-%u, offsetOfGCDataPointer-%u", + ", offsetOfThreadStaticBlocks-%u, offsetOfGCDataPointer-%u", key, SpmiDumpHelper::DumpAgnostic_CORINFO_CONST_LOOKUP(value.tlsIndex).c_str(), value.tlsGetAddrFtnPtr, value.tlsIndexObject, value.threadVarsSection, value.offsetOfThreadLocalStoragePointer, value.offsetOfMaxThreadStaticBlocks, value.offsetOfThreadStaticBlocks, value.offsetOfGCDataPointer); @@ -3652,7 +3652,7 @@ void MethodContext::repGetThreadLocalStaticBlocksInfo(CORINFO_THREAD_STATIC_BLOC pInfo->tlsIndexObject = (void*)value.tlsIndexObject; pInfo->threadVarsSection = (void*)value.threadVarsSection; pInfo->offsetOfThreadLocalStoragePointer = value.offsetOfThreadLocalStoragePointer; - pInfo->offsetOfMaxThreadStaticBlocks = value.offsetOfMaxThreadStaticBlocks; + pInfo->offsetOfMaxThreadStaticBlocks = value.offsetOfMaxThreadStaticBlocks; pInfo->offsetOfThreadStaticBlocks = value.offsetOfThreadStaticBlocks; pInfo->offsetOfGCDataPointer = value.offsetOfGCDataPointer; } @@ -4370,7 +4370,7 @@ void MethodContext::repGetEEInfo(CORINFO_EE_INFO* pEEInfoOut) pEEInfoOut->offsetOfThreadFrame = (unsigned)0x10; pEEInfoOut->offsetOfGCState = (unsigned)0xc; pEEInfoOut->offsetOfDelegateInstance = (unsigned)0x8; - pEEInfoOut->offsetOfDelegateFirstTarget = (unsigned)0x10; + pEEInfoOut->offsetOfDelegateFirstTarget = (unsigned)0x18; pEEInfoOut->offsetOfWrapperDelegateIndirectCell = (unsigned)0x40; pEEInfoOut->sizeOfReversePInvokeFrame = (unsigned)0x8; pEEInfoOut->osPageSize = (size_t)0x1000; diff --git a/src/coreclr/vm/amd64/asmconstants.h b/src/coreclr/vm/amd64/asmconstants.h index 277fe6b23816b4..f19d2812636e63 100644 --- a/src/coreclr/vm/amd64/asmconstants.h +++ b/src/coreclr/vm/amd64/asmconstants.h @@ -138,7 +138,7 @@ ASMCONSTANTS_C_ASSERT(OFFSETOF__ThreadExceptionState__m_pCurrentTracker -#define OFFSETOF__DelegateObject___methodPtr 0x10 +#define OFFSETOF__DelegateObject___methodPtr 0x18 ASMCONSTANT_OFFSETOF_ASSERT(DelegateObject, _methodPtr); #define OFFSETOF__DelegateObject___target 0x08 diff --git a/src/coreclr/vm/arm/asmconstants.h b/src/coreclr/vm/arm/asmconstants.h index 76b583c8e2893a..16931168e3ce0d 100644 --- a/src/coreclr/vm/arm/asmconstants.h +++ b/src/coreclr/vm/arm/asmconstants.h @@ -64,7 +64,7 @@ ASMCONSTANTS_C_ASSERT(LazyMachState_captureSp == offsetof(LazyMachState, capture #define LazyMachState_captureIp (LazyMachState_captureSp+4) ASMCONSTANTS_C_ASSERT(LazyMachState_captureIp == offsetof(LazyMachState, captureIp)) -#define DelegateObject___methodPtr 0x08 +#define DelegateObject___methodPtr 0x0c ASMCONSTANTS_C_ASSERT(DelegateObject___methodPtr == offsetof(DelegateObject, _methodPtr)); #define DelegateObject___target 0x04 diff --git a/src/coreclr/vm/arm64/asmconstants.h b/src/coreclr/vm/arm64/asmconstants.h index 5b67217d47d4a8..cc19728d9e29ee 100644 --- a/src/coreclr/vm/arm64/asmconstants.h +++ b/src/coreclr/vm/arm64/asmconstants.h @@ -109,7 +109,7 @@ ASMCONSTANTS_C_ASSERT(LazyMachState_captureIp == offsetof(LazyMachState, capture #define VASigCookie__pNDirectILStub 0x8 ASMCONSTANTS_C_ASSERT(VASigCookie__pNDirectILStub == offsetof(VASigCookie, pNDirectILStub)) -#define DelegateObject___methodPtr 0x10 +#define DelegateObject___methodPtr 0x18 ASMCONSTANTS_C_ASSERT(DelegateObject___methodPtr == offsetof(DelegateObject, _methodPtr)); #define DelegateObject___target 0x08 diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 12b5b6b1d1ac2c..b40a3b79117a23 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -247,14 +247,14 @@ DEFINE_METHOD(DATE_TIME, LONG_CTOR, .ctor, DEFINE_CLASS(DECIMAL, System, Decimal) DEFINE_METHOD(DECIMAL, CURRENCY_CTOR, .ctor, IM_Currency_RetVoid) -DEFINE_CLASS_U(System, Delegate, NoClass) -DEFINE_FIELD_U(_target, DelegateObject, _target) -DEFINE_FIELD_U(_methodPtr, DelegateObject, _methodPtr) -DEFINE_FIELD_U(_methodPtrAux, DelegateObject, _methodPtrAux) +DEFINE_CLASS_U(System, Delegate, NoClass) +DEFINE_FIELD_U(_target, DelegateObject, _target) +DEFINE_FIELD_U(_methodPtrAux, DelegateObject, _methodPtrAux) +DEFINE_FIELD_U(_methodPtr, DelegateObject, _methodPtr) DEFINE_CLASS(DELEGATE, System, Delegate) -DEFINE_FIELD(DELEGATE, TARGET, _target) -DEFINE_FIELD(DELEGATE, METHOD_PTR, _methodPtr) -DEFINE_FIELD(DELEGATE, METHOD_PTR_AUX, _methodPtrAux) +DEFINE_FIELD(DELEGATE, TARGET, _target) +DEFINE_FIELD(DELEGATE, METHOD_PTR_AUX, _methodPtrAux) +DEFINE_FIELD(DELEGATE, METHOD_PTR, _methodPtr) DEFINE_METHOD(DELEGATE, CONSTRUCT_DELEGATE, DelegateConstruct, IM_Obj_IntPtr_RetVoid) DEFINE_METHOD(DELEGATE, GET_INVOKE_METHOD, GetInvokeMethod, IM_RetIntPtr) DEFINE_METHOD(DELEGATE, SET_CACHED_METHOD, SetCachedMethod, IM_Obj_RetVoid) diff --git a/src/coreclr/vm/i386/asmconstants.h b/src/coreclr/vm/i386/asmconstants.h index 1ab9406b8ec6c9..d766ee8591ea3e 100644 --- a/src/coreclr/vm/i386/asmconstants.h +++ b/src/coreclr/vm/i386/asmconstants.h @@ -301,8 +301,8 @@ ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_pCalleeSavedFP == offsetof(InlinedCall #ifdef FEATURE_STUBS_AS_IL // DelegateObject from src/vm/object.h #define DelegateObject___target 0x04 // offset 0 is m_pMethTab of base class Object -#define DelegateObject___methodPtr 0x08 -#define DelegateObject___methodPtrAux 0x0c +#define DelegateObject___methodPtrAux 0x08 +#define DelegateObject___methodPtr 0x0c #define DelegateObject___invocationList 0x10 #define DelegateObject___invocationCount 0x14 diff --git a/src/coreclr/vm/loongarch64/asmconstants.h b/src/coreclr/vm/loongarch64/asmconstants.h index 2d47ceea2f2675..e12d0040a74d0a 100644 --- a/src/coreclr/vm/loongarch64/asmconstants.h +++ b/src/coreclr/vm/loongarch64/asmconstants.h @@ -134,7 +134,7 @@ ASMCONSTANTS_C_ASSERT(LazyMachState_captureIp == offsetof(LazyMachState, capture #define VASigCookie__pNDirectILStub 0x8 ASMCONSTANTS_C_ASSERT(VASigCookie__pNDirectILStub == offsetof(VASigCookie, pNDirectILStub)) -#define DelegateObject___methodPtr 0x10 +#define DelegateObject___methodPtr 0x18 ASMCONSTANTS_C_ASSERT(DelegateObject___methodPtr == offsetof(DelegateObject, _methodPtr)); #define DelegateObject___target 0x08 diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index 5617cf84e9a015..8da977ba7fcafb 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -1984,14 +1984,14 @@ class DelegateObject : public Object void SetTarget(OBJECTREF target) { WRAPPER_NO_CONTRACT; SetObjectReference(&_target, target); } static int GetOffsetOfTarget() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _target); } - PCODE GetMethodPtr() { LIMITED_METHOD_CONTRACT; return _methodPtr; } - void SetMethodPtr(PCODE methodPtr) { LIMITED_METHOD_CONTRACT; _methodPtr = methodPtr; } - static int GetOffsetOfMethodPtr() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtr); } - PCODE GetMethodPtrAux() { LIMITED_METHOD_CONTRACT; return _methodPtrAux; } void SetMethodPtrAux(PCODE methodPtrAux) { LIMITED_METHOD_CONTRACT; _methodPtrAux = methodPtrAux; } static int GetOffsetOfMethodPtrAux() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtrAux); } + PCODE GetMethodPtr() { LIMITED_METHOD_CONTRACT; return _methodPtr; } + void SetMethodPtr(PCODE methodPtr) { LIMITED_METHOD_CONTRACT; _methodPtr = methodPtr; } + static int GetOffsetOfMethodPtr() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtr); } + OBJECTREF GetInvocationList() { LIMITED_METHOD_CONTRACT; return _invocationList; } void SetInvocationList(OBJECTREF invocationList) { WRAPPER_NO_CONTRACT; SetObjectReference(&_invocationList, invocationList); } static int GetOffsetOfInvocationList() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _invocationList); } @@ -2008,16 +2008,16 @@ class DelegateObject : public Object private: // System.Delegate OBJECTREF _target; - PCODE _methodPtr; PCODE _methodPtrAux; + PCODE _methodPtr; // System.MulticastDelegate OBJECTREF _invocationList; INT_PTR _invocationCount; }; #define OFFSETOF__DelegateObject__target OBJECT_SIZE /* m_pMethTab */ -#define OFFSETOF__DelegateObject__methodPtr (OFFSETOF__DelegateObject__target + TARGET_POINTER_SIZE /* _target */) -#define OFFSETOF__DelegateObject__methodPtrAux (OFFSETOF__DelegateObject__methodPtr + TARGET_POINTER_SIZE /* _methodPtr */) +#define OFFSETOF__DelegateObject__methodPtrAux (OFFSETOF__DelegateObject__target + TARGET_POINTER_SIZE /* _target */) +#define OFFSETOF__DelegateObject__methodPtr (OFFSETOF__DelegateObject__methodPtrAux + TARGET_POINTER_SIZE /* _methodPtrAux */) #ifdef USE_CHECKED_OBJECTREFS typedef REF DELEGATEREF; diff --git a/src/coreclr/vm/riscv64/asmconstants.h b/src/coreclr/vm/riscv64/asmconstants.h index 57f9a63fbbd172..71095e3cffc994 100644 --- a/src/coreclr/vm/riscv64/asmconstants.h +++ b/src/coreclr/vm/riscv64/asmconstants.h @@ -129,7 +129,7 @@ ASMCONSTANTS_C_ASSERT(LazyMachState_captureIp == offsetof(LazyMachState, capture #define VASigCookie__pNDirectILStub 0x8 ASMCONSTANTS_C_ASSERT(VASigCookie__pNDirectILStub == offsetof(VASigCookie, pNDirectILStub)) -#define DelegateObject___methodPtr 0x10 +#define DelegateObject___methodPtr 0x18 ASMCONSTANTS_C_ASSERT(DelegateObject___methodPtr == offsetof(DelegateObject, _methodPtr)); #define DelegateObject___target 0x08 From 90c6085a11f4e7d076a23810c0f85d321f4d3c56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Mon, 4 Mar 2024 17:03:48 +0100 Subject: [PATCH 10/77] Reorder asserts --- src/coreclr/vm/i386/asmconstants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/i386/asmconstants.h b/src/coreclr/vm/i386/asmconstants.h index d766ee8591ea3e..346da3308eff1d 100644 --- a/src/coreclr/vm/i386/asmconstants.h +++ b/src/coreclr/vm/i386/asmconstants.h @@ -307,8 +307,8 @@ ASMCONSTANTS_C_ASSERT(InlinedCallFrame__m_pCalleeSavedFP == offsetof(InlinedCall #define DelegateObject___invocationCount 0x14 ASMCONSTANTS_C_ASSERT(DelegateObject___target == offsetof(DelegateObject, _target)); -ASMCONSTANTS_C_ASSERT(DelegateObject___methodPtr == offsetof(DelegateObject, _methodPtr)); ASMCONSTANTS_C_ASSERT(DelegateObject___methodPtrAux == offsetof(DelegateObject, _methodPtrAux)); +ASMCONSTANTS_C_ASSERT(DelegateObject___methodPtr == offsetof(DelegateObject, _methodPtr)); ASMCONSTANTS_C_ASSERT(DelegateObject___invocationList == offsetof(DelegateObject, _invocationList)); ASMCONSTANTS_C_ASSERT(DelegateObject___invocationCount == offsetof(DelegateObject, _invocationCount)); From 95090234cf135ec8e92cee0066a891873bd0ff1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 4 Mar 2024 19:12:34 +0100 Subject: [PATCH 11/77] Fix contract again --- src/coreclr/vm/object.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/object.cpp b/src/coreclr/vm/object.cpp index a5c7cbc82bac27..c37f3619243a40 100644 --- a/src/coreclr/vm/object.cpp +++ b/src/coreclr/vm/object.cpp @@ -1398,7 +1398,7 @@ void DelegateObject::SetMethodBase(OBJECTREF newMethodBase) { CONTRACTL { - NOTHROW; + THROWS; GC_TRIGGERS; MODE_COOPERATIVE; } From 557bf4da6ab528df83eb2d740cbd7e741d53635c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 4 Mar 2024 21:27:40 +0100 Subject: [PATCH 12/77] GCPROTECT --- src/coreclr/vm/object.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/object.cpp b/src/coreclr/vm/object.cpp index c37f3619243a40..78e93dbda64af7 100644 --- a/src/coreclr/vm/object.cpp +++ b/src/coreclr/vm/object.cpp @@ -1403,12 +1403,14 @@ void DelegateObject::SetMethodBase(OBJECTREF newMethodBase) MODE_COOPERATIVE; } CONTRACTL_END; - + + GCPROTECT_BEGIN(newMethodBase); PREPARE_NONVIRTUAL_CALLSITE(METHOD__DELEGATE__SET_CACHED_METHOD); DECLARE_ARGHOLDER_ARRAY(args, 2); args[ARGNUM_0] = PTR_TO_ARGHOLDER(this); args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(newMethodBase); CALL_MANAGED_METHOD_NORET(args); + GCPROTECT_END(); } void StackTraceArray::Append(StackTraceElement const * begin, StackTraceElement const * end) From 2c16705a2d2adf82d98062b8e33b817de64c35c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Wed, 16 Oct 2024 20:41:02 +0200 Subject: [PATCH 13/77] Rework equality checks --- .../src/System/Delegate.CoreCLR.cs | 70 ++++++++++--------- .../src/System/MulticastDelegate.CoreCLR.cs | 16 +---- src/coreclr/debug/daccess/dacdbiimpl.cpp | 2 +- src/coreclr/vm/comdelegate.cpp | 24 ++++--- src/coreclr/vm/comdelegate.h | 4 +- src/coreclr/vm/object.cpp | 19 ----- src/coreclr/vm/object.h | 20 +++--- src/coreclr/vm/qcallentrypoints.cpp | 4 +- 8 files changed, 71 insertions(+), 88 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 0a76d7f27109b8..e63d836bde93f2 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -17,15 +17,28 @@ public abstract partial class Delegate : ICloneable, ISerializable // _target is the object we will invoke on internal object? _target; // Initialized by VM as needed; null if static delegate + // VM handle to wrapped method, null when not initialized + private IntPtr _methodDesc; + + // _methodPtr is a pointer to the method we will invoke + // It could be a small thunk if this is a static or UM call + internal IntPtr _methodPtr; + // In the case of a static method passed to a delegate, this field stores // whatever _methodPtr would have stored: and _methodPtr points to a // small thunk which removes the "this" pointer before going on // to _methodPtrAux. internal IntPtr _methodPtrAux; - // _methodPtr is a pointer to the method we will invoke - // It could be a small thunk if this is a static or UM call - internal IntPtr _methodPtr; + internal IntPtr MethodDesc + { + get + { + if (_methodDesc == 0) + _methodDesc = GetMethodDesc(); + return _methodDesc; + } + } // This constructor is called from the class generated by the // compiler generated code @@ -126,11 +139,7 @@ public override bool Equals([NotNullWhen(true)] object? obj) // fall through method handle check } - // method ptrs don't match, go down long path - if (Cache.s_methodCache.TryGetValue(this, out object? thisCache) && thisCache is MethodInfo thisMethod && - Cache.s_methodCache.TryGetValue(d, out object? otherCache) && otherCache is MethodInfo otherMethod) - return thisMethod.Equals(otherMethod); - return InternalEqualMethodHandles(this, d); + return MethodDesc == d.MethodDesc; } public override int GetHashCode() @@ -151,21 +160,16 @@ public override int GetHashCode() return GetType().GetHashCode(); } - protected virtual MethodInfo GetMethodImpl() + protected MethodInfo GetMethodImpl() { - if (Cache.s_methodCache.TryGetValue(this, out object? cachedValue) && cachedValue is MethodInfo methodInfo) - { - return methodInfo; - } - - return GetMethodImplUncached(); + return Cache.s_methodCache.TryGetValue(this, out MethodInfo? cachedValue) ? cachedValue : GetMethodImplUncached(); } - internal MethodInfo GetMethodImplUncached() + protected virtual MethodInfo GetMethodImplUncached() { - IRuntimeMethodInfo method = FindMethodHandle(); + IRuntimeMethodInfo method = CreateMethodInfo(MethodDesc); RuntimeType? declaringType = RuntimeMethodHandle.GetDeclaringType(method); - + // need a proper declaring type instance method on a generic type if (declaringType.IsGenericType) { @@ -218,10 +222,10 @@ internal MethodInfo GetMethodImplUncached() return methodInfo; } - // this is called by the VM in addition to calls from managed code - internal void SetCachedMethod(object? value) + protected void SetCachedMethod(MethodInfo methodInfo) { - Cache.s_methodCache.AddOrUpdate(this, value); + Debug.Assert(methodInfo is not null); + Cache.s_methodCache.AddOrUpdate(this, methodInfo); } public object? Target => GetTarget(); @@ -531,25 +535,25 @@ internal unsafe IntPtr GetInvokeMethod() return (IntPtr)ptr; } - internal IRuntimeMethodInfo FindMethodHandle() + internal static IRuntimeMethodInfo CreateMethodInfo(IntPtr methodDesc) { - Delegate d = this; IRuntimeMethodInfo? methodInfo = null; - FindMethodHandle(ObjectHandleOnStack.Create(ref d), ObjectHandleOnStack.Create(ref methodInfo)); + CreateMethodInfo(methodDesc, ObjectHandleOnStack.Create(ref methodInfo)); return methodInfo!; } - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_FindMethodHandle")] - private static partial void FindMethodHandle(ObjectHandleOnStack d, ObjectHandleOnStack retMethodInfo); + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_CreateMethodInfo")] + private static partial void CreateMethodInfo(IntPtr methodDesc, ObjectHandleOnStack retMethodInfo); - private static bool InternalEqualMethodHandles(Delegate left, Delegate right) + private IntPtr GetMethodDesc() { - return InternalEqualMethodHandles(ObjectHandleOnStack.Create(ref left), ObjectHandleOnStack.Create(ref right)); + Delegate d = this; + return GetMethodDesc(ObjectHandleOnStack.Create(ref d)); } - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_InternalEqualMethodHandles")] + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_GetMethodDesc")] [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool InternalEqualMethodHandles(ObjectHandleOnStack left, ObjectHandleOnStack right); + private static partial IntPtr GetMethodDesc(ObjectHandleOnStack instance); internal static IntPtr AdjustTarget(object target, IntPtr methodPtr) { @@ -574,11 +578,9 @@ internal void InitializeVirtualCallStub(IntPtr methodPtr) } // Caches MethodInfos, added either after first request or assigned from a DynamicMethod - // For open delegates to collectible types, this may contain a LoaderAllocator object - // that prevents the type from being unloaded. - internal static class Cache + private static class Cache { - public static readonly ConditionalWeakTable s_methodCache = new(); + public static readonly ConditionalWeakTable s_methodCache = new(); } } diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index c12fe823f3d8dd..6bafe85259a8a9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -494,17 +494,7 @@ public sealed override int GetHashCode() return base.GetTarget(); } - protected override MethodInfo GetMethodImpl() - { - if (Cache.s_methodCache.TryGetValue(this, out object? cachedValue) && cachedValue is MethodInfo methodInfo) - { - return methodInfo; - } - - return GetMulticastMethodImplUncached(); - } - - internal MethodInfo GetMulticastMethodImplUncached() + protected override MethodInfo GetMethodImplUncached() { if (_invocationCount != 0 && _invocationList != null) { @@ -525,7 +515,7 @@ internal MethodInfo GetMulticastMethodImplUncached() { // we handle unmanaged function pointers here because the generic ones (used for WinRT) would otherwise // be treated as open delegates by the base implementation, resulting in failure to get the MethodInfo - IRuntimeMethodInfo method = FindMethodHandle(); + IRuntimeMethodInfo method = CreateMethodInfo(MethodDesc); RuntimeType declaringType = RuntimeMethodHandle.GetDeclaringType(method); // need a proper declaring type instance method on a generic type @@ -542,7 +532,7 @@ internal MethodInfo GetMulticastMethodImplUncached() } // Otherwise, must be an inner delegate of a wrapper delegate of an open virtual method. In that case, call base implementation - return GetMethodImplUncached(); + return base.GetMethodImplUncached(); } // this should help inlining diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 770e81a9480e14..3b0f008632d9d6 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -3390,7 +3390,7 @@ HRESULT DacDbiInterfaceImpl::GetDelegateType(VMPTR_Object delegateObject, Delega // several pieces of logic so this replicates the logic mostly due to time constraints. The Mainly from: // - System.Private.CoreLib!System.Delegate.GetMethodImpl and System.Private.CoreLib!System.MulticastDelegate.GetMethodImpl // - System.Private.CoreLib!System.Delegate.GetTarget and System.Private.CoreLib!System.MulticastDelegate.GetTarget - // - coreclr!COMDelegate::GetMethodDesc and coreclr!COMDelegate::FindMethodHandle + // - coreclr!COMDelegate::GetMethodDesc and coreclr!COMDelegate::CreateMethodInfo // - coreclr!Delegate_Construct and the delegate type table in // - DELEGATE KINDS TABLE in comdelegate.cpp diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 97af956c1f26d8..80b4dc74cbf256 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1090,6 +1090,8 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, pTargetMethod->EnsureActive(); + refRealDelegate->SetMethodDesc(pTargetMethod); + if (fIsOpenDelegate) { _ASSERTE(pRefFirstArg == NULL || *pRefFirstArg == NULL); @@ -1190,7 +1192,7 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, LoaderAllocator *pLoaderAllocator = pTargetMethod->GetLoaderAllocator(); if (pLoaderAllocator->IsCollectible()) - refRealDelegate->SetMethodBase(pLoaderAllocator->GetExposedObject()); + refRealDelegate->SetInvocationList(pLoaderAllocator->GetExposedObject()); GCPROTECT_END(); } @@ -1403,6 +1405,8 @@ OBJECTREF COMDelegate::ConvertToDelegate(LPVOID pCallback, MethodTable* pMT) // Wire up the unmanaged call stub to the delegate. delObj->SetTarget(delObj); // We are the "this" object + delObj->SetMethodDesc(pMD); + // For X86, we save the entry point in the delegate's method pointer and the UM Callsite in the aux pointer. delObj->SetMethodPtr(pMarshalStub); delObj->SetMethodPtrAux((PCODE)pCallback); @@ -1624,8 +1628,10 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q if (COMDelegate::NeedsWrapperDelegate(pMeth)) refThis = COMDelegate::CreateWrapperDelegate(refThis, pMeth); + refThis->SetMethodDesc(pMethOrig); + if (pMeth->GetLoaderAllocator()->IsCollectible()) - refThis->SetMethodBase(pMeth->GetLoaderAllocator()->GetExposedObject()); + refThis->SetInvocationList(pMeth->GetLoaderAllocator()->GetExposedObject()); // Open delegates. if (invokeArgCount == methodArgCount) @@ -2037,6 +2043,8 @@ DELEGATEREF COMDelegate::CreateWrapperDelegate(DELEGATEREF delegate, MethodDesc* // save the secure invoke stub. GetWrapperInvoke() can trigger GC. PCODE tmp = GetWrapperInvoke(pMD); + + gc.refWrapperDel->SetMethodDesc(pMD); gc.refWrapperDel->SetMethodPtr(tmp); // save the delegate MethodDesc for the frame gc.refWrapperDel->SetInvocationCount((INT_PTR)pMD); @@ -2051,7 +2059,7 @@ DELEGATEREF COMDelegate::CreateWrapperDelegate(DELEGATEREF delegate, MethodDesc* } // This method will get the MethodInfo for a delegate -extern "C" void QCALLTYPE Delegate_FindMethodHandle(QCall::ObjectHandleOnStack d, QCall::ObjectHandleOnStack retMethodInfo) +extern "C" void QCALLTYPE Delegate_CreateMethodInfo(MethodDesc* methodDesc, QCall::ObjectHandleOnStack retMethodInfo) { QCALL_CONTRACT; @@ -2059,26 +2067,24 @@ extern "C" void QCALLTYPE Delegate_FindMethodHandle(QCall::ObjectHandleOnStack d GCX_COOP(); - MethodDesc* pMD = COMDelegate::GetMethodDesc(d.Get()); + MethodDesc* pMD = methodDesc; pMD = MethodDesc::FindOrCreateAssociatedMethodDescForReflection(pMD, TypeHandle(pMD->GetMethodTable()), pMD->GetMethodInstantiation()); retMethodInfo.Set(pMD->GetStubMethodInfo()); END_QCALL; } -extern "C" BOOL QCALLTYPE Delegate_InternalEqualMethodHandles(QCall::ObjectHandleOnStack left, QCall::ObjectHandleOnStack right) +extern "C" MethodDesc* QCALLTYPE Delegate_GetMethodDesc(QCall::ObjectHandleOnStack instance) { QCALL_CONTRACT; - BOOL fRet = FALSE; + MethodDesc* fRet = nullptr; BEGIN_QCALL; GCX_COOP(); - MethodDesc* pMDLeft = COMDelegate::GetMethodDesc(left.Get()); - MethodDesc* pMDRight = COMDelegate::GetMethodDesc(right.Get()); - fRet = pMDLeft == pMDRight; + fRet = COMDelegate::GetMethodDesc(instance.Get()); END_QCALL; diff --git a/src/coreclr/vm/comdelegate.h b/src/coreclr/vm/comdelegate.h index be68b5ef40dd47..7df931c4b9d9f9 100644 --- a/src/coreclr/vm/comdelegate.h +++ b/src/coreclr/vm/comdelegate.h @@ -127,9 +127,9 @@ extern "C" void QCALLTYPE Delegate_InternalAlloc(QCall::TypeHandle pType, QCall: extern "C" void QCALLTYPE Delegate_InternalAllocLike(QCall::ObjectHandleOnStack d); -extern "C" void QCALLTYPE Delegate_FindMethodHandle(QCall::ObjectHandleOnStack d, QCall::ObjectHandleOnStack retMethodInfo); +extern "C" void QCALLTYPE Delegate_CreateMethodInfo(MethodDesc* methodDesc, QCall::ObjectHandleOnStack retMethodInfo); -extern "C" BOOL QCALLTYPE Delegate_InternalEqualMethodHandles(QCall::ObjectHandleOnStack left, QCall::ObjectHandleOnStack right); +extern "C" MethodDesc* QCALLTYPE Delegate_GetMethodDesc(QCall::ObjectHandleOnStack instance); void DistributeEvent(OBJECTREF *pDelegate, diff --git a/src/coreclr/vm/object.cpp b/src/coreclr/vm/object.cpp index 2efd6fc1bf1215..050f16e09ad04e 100644 --- a/src/coreclr/vm/object.cpp +++ b/src/coreclr/vm/object.cpp @@ -1426,25 +1426,6 @@ void __fastcall ZeroMemoryInGCHeap(void* mem, size_t size) *memBytes++ = 0; } -void DelegateObject::SetMethodBase(OBJECTREF newMethodBase) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - GCPROTECT_BEGIN(newMethodBase); - PREPARE_NONVIRTUAL_CALLSITE(METHOD__DELEGATE__SET_CACHED_METHOD); - DECLARE_ARGHOLDER_ARRAY(args, 2); - args[ARGNUM_0] = PTR_TO_ARGHOLDER(this); - args[ARGNUM_1] = OBJECTREF_TO_ARGHOLDER(newMethodBase); - CALL_MANAGED_METHOD_NORET(args); - GCPROTECT_END(); -} - void StackTraceArray::Append(StackTraceElement const * elem) { CONTRACTL diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index 6054f8ea23c4f0..697227da77cc72 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -1875,14 +1875,18 @@ class DelegateObject : public Object void SetTarget(OBJECTREF target) { WRAPPER_NO_CONTRACT; SetObjectReference(&_target, target); } static int GetOffsetOfTarget() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _target); } - PCODE GetMethodPtrAux() { LIMITED_METHOD_CONTRACT; return _methodPtrAux; } - void SetMethodPtrAux(PCODE methodPtrAux) { LIMITED_METHOD_CONTRACT; _methodPtrAux = methodPtrAux; } - static int GetOffsetOfMethodPtrAux() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtrAux); } + MethodDesc* GetMethodDesc() { LIMITED_METHOD_CONTRACT; return _methodDesc; } + void SetMethodDesc(MethodDesc* methodPtrAux) { LIMITED_METHOD_CONTRACT; _methodDesc = methodPtrAux; } + static int GetOffsetOfMethoddDesc() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodDesc); } PCODE GetMethodPtr() { LIMITED_METHOD_CONTRACT; return _methodPtr; } void SetMethodPtr(PCODE methodPtr) { LIMITED_METHOD_CONTRACT; _methodPtr = methodPtr; } static int GetOffsetOfMethodPtr() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtr); } + PCODE GetMethodPtrAux() { LIMITED_METHOD_CONTRACT; return _methodPtrAux; } + void SetMethodPtrAux(PCODE methodPtrAux) { LIMITED_METHOD_CONTRACT; _methodPtrAux = methodPtrAux; } + static int GetOffsetOfMethodPtrAux() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtrAux); } + OBJECTREF GetInvocationList() { LIMITED_METHOD_CONTRACT; return _invocationList; } void SetInvocationList(OBJECTREF invocationList) { WRAPPER_NO_CONTRACT; SetObjectReference(&_invocationList, invocationList); } static int GetOffsetOfInvocationList() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _invocationList); } @@ -1891,24 +1895,24 @@ class DelegateObject : public Object void SetInvocationCount(INT_PTR invocationCount) { LIMITED_METHOD_CONTRACT; _invocationCount = invocationCount; } static int GetOffsetOfInvocationCount() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _invocationCount); } - void SetMethodBase(OBJECTREF newMethodBase); - // README: // If you modify the order of these fields, make sure to update the definition in // BCL for this object. private: // System.Delegate OBJECTREF _target; - PCODE _methodPtrAux; + MethodDesc* _methodDesc; PCODE _methodPtr; + PCODE _methodPtrAux; // System.MulticastDelegate OBJECTREF _invocationList; INT_PTR _invocationCount; }; #define OFFSETOF__DelegateObject__target OBJECT_SIZE /* m_pMethTab */ -#define OFFSETOF__DelegateObject__methodPtrAux (OFFSETOF__DelegateObject__target + TARGET_POINTER_SIZE /* _target */) -#define OFFSETOF__DelegateObject__methodPtr (OFFSETOF__DelegateObject__methodPtrAux + TARGET_POINTER_SIZE /* _methodPtrAux */) +#define OFFSETOF__DelegateObject__methodDesc (OFFSETOF__DelegateObject__target + TARGET_POINTER_SIZE /* _target */) +#define OFFSETOF__DelegateObject__methodPtr (OFFSETOF__DelegateObject__methodDesc + TARGET_POINTER_SIZE /* _methodDesc */) +#define OFFSETOF__DelegateObject__methodPtrAux (OFFSETOF__DelegateObject__methodPtr + TARGET_POINTER_SIZE /* _methodPtr */) #ifdef USE_CHECKED_OBJECTREFS typedef REF DELEGATEREF; diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index dcc7df91acf2dd..6722d9f382862f 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -96,8 +96,8 @@ static const Entry s_QCall[] = DllImportEntry(Delegate_Construct) DllImportEntry(Delegate_InternalAlloc) DllImportEntry(Delegate_InternalAllocLike) - DllImportEntry(Delegate_FindMethodHandle) - DllImportEntry(Delegate_InternalEqualMethodHandles) + DllImportEntry(Delegate_CreateMethodInfo) + DllImportEntry(Delegate_GetMethodDesc) DllImportEntry(Environment_Exit) DllImportEntry(Environment_FailFast) DllImportEntry(Environment_GetProcessorCount) From bb2a509eca3d91f161738ccdb556011d364912e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Wed, 16 Oct 2024 21:48:26 +0200 Subject: [PATCH 14/77] Fix compile errors --- .../System.Private.CoreLib/src/System/Delegate.CoreCLR.cs | 7 +++---- .../src/System/MulticastDelegate.CoreCLR.cs | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index e63d836bde93f2..1ede2f9579f335 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -160,12 +160,12 @@ public override int GetHashCode() return GetType().GetHashCode(); } - protected MethodInfo GetMethodImpl() + protected virtual MethodInfo GetMethodImpl() { return Cache.s_methodCache.TryGetValue(this, out MethodInfo? cachedValue) ? cachedValue : GetMethodImplUncached(); } - protected virtual MethodInfo GetMethodImplUncached() + internal virtual MethodInfo GetMethodImplUncached() { IRuntimeMethodInfo method = CreateMethodInfo(MethodDesc); RuntimeType? declaringType = RuntimeMethodHandle.GetDeclaringType(method); @@ -222,7 +222,7 @@ protected virtual MethodInfo GetMethodImplUncached() return methodInfo; } - protected void SetCachedMethod(MethodInfo methodInfo) + internal void SetCachedMethod(MethodInfo methodInfo) { Debug.Assert(methodInfo is not null); Cache.s_methodCache.AddOrUpdate(this, methodInfo); @@ -552,7 +552,6 @@ private IntPtr GetMethodDesc() } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_GetMethodDesc")] - [return: MarshalAs(UnmanagedType.Bool)] private static partial IntPtr GetMethodDesc(ObjectHandleOnStack instance); internal static IntPtr AdjustTarget(object target, IntPtr methodPtr) diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index 6bafe85259a8a9..b53c49c8185ea9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -494,7 +494,7 @@ public sealed override int GetHashCode() return base.GetTarget(); } - protected override MethodInfo GetMethodImplUncached() + internal override MethodInfo GetMethodImplUncached() { if (_invocationCount != 0 && _invocationList != null) { @@ -594,7 +594,7 @@ private void CtorCollectibleClosedStatic(object target, IntPtr methodPtr, IntPtr { this._target = target; this._methodPtr = methodPtr; - this.SetCachedMethod(GCHandle.InternalGet(gchandle)); + this._invocationList = GCHandle.InternalGet(gchandle); } [DebuggerNonUserCode] @@ -604,7 +604,7 @@ private void CtorCollectibleOpened(object target, IntPtr methodPtr, IntPtr shuff this._target = this; this._methodPtr = shuffleThunk; this._methodPtrAux = methodPtr; - this.SetCachedMethod(GCHandle.InternalGet(gchandle)); + this._invocationList = GCHandle.InternalGet(gchandle); } [DebuggerNonUserCode] @@ -613,7 +613,7 @@ private void CtorCollectibleVirtualDispatch(object target, IntPtr methodPtr, Int { this._target = this; this._methodPtr = shuffleThunk; - this.SetCachedMethod(GCHandle.InternalGet(gchandle)); + this._invocationList = GCHandle.InternalGet(gchandle); this.InitializeVirtualCallStub(methodPtr); } #pragma warning restore IDE0060 From 3bb047b60ca1ac7d8ab9aac083ca96d98db1f8dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Wed, 16 Oct 2024 21:53:04 +0200 Subject: [PATCH 15/77] Add asserts --- .../src/System/MulticastDelegate.CoreCLR.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index b53c49c8185ea9..ffe5cffa0f4ca0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -595,6 +595,7 @@ private void CtorCollectibleClosedStatic(object target, IntPtr methodPtr, IntPtr this._target = target; this._methodPtr = methodPtr; this._invocationList = GCHandle.InternalGet(gchandle); + Debug.Assert(InvocationListLogicallyNull()); } [DebuggerNonUserCode] @@ -605,6 +606,7 @@ private void CtorCollectibleOpened(object target, IntPtr methodPtr, IntPtr shuff this._methodPtr = shuffleThunk; this._methodPtrAux = methodPtr; this._invocationList = GCHandle.InternalGet(gchandle); + Debug.Assert(InvocationListLogicallyNull()); } [DebuggerNonUserCode] @@ -614,6 +616,7 @@ private void CtorCollectibleVirtualDispatch(object target, IntPtr methodPtr, Int this._target = this; this._methodPtr = shuffleThunk; this._invocationList = GCHandle.InternalGet(gchandle); + Debug.Assert(InvocationListLogicallyNull()); this.InitializeVirtualCallStub(methodPtr); } #pragma warning restore IDE0060 From b9573922b516fa548d5ae116e0286fbf6053d786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Wed, 16 Oct 2024 22:12:19 +0200 Subject: [PATCH 16/77] Revert some changes, add more caching --- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 2 +- src/coreclr/vm/amd64/asmconstants.h | 8 ++++---- src/coreclr/vm/comdelegate.cpp | 12 ++++++++++-- src/coreclr/vm/corelib.h | 7 ++++--- src/coreclr/vm/object.h | 2 +- .../System.Private.CoreLib/src/System/Delegate.cs | 6 +++--- 6 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 5ebb9e14d20590..eebbbaceb7c2f0 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -2111,7 +2111,7 @@ private void ceeInfoGetCallInfo( // of shared generic code calling a shared generic implementation method, which should be rare. // // An alternative design would be to add a new generic dictionary entry kind to hold the MethodDesc - // of the constrained target instead, and use that in some circumstances; however, implementation of + // of the constrained target instead, and use that in some circumstances; however, implementation of // that design requires refactoring variuos parts of the JIT interface as well as // TryResolveConstraintMethodApprox. In particular we would need to be abled to embed a constrained lookup // via EmbedGenericHandle, as well as decide in TryResolveConstraintMethodApprox if the call can be made diff --git a/src/coreclr/vm/amd64/asmconstants.h b/src/coreclr/vm/amd64/asmconstants.h index 7d1fb0d4740698..8815f9ca8b354c 100644 --- a/src/coreclr/vm/amd64/asmconstants.h +++ b/src/coreclr/vm/amd64/asmconstants.h @@ -631,13 +631,13 @@ template class FindCompileTimeConstant { private: - FindCompileTimeConstant(); + FindCompileTimeConstant(); }; void BogusFunction() { - // Sample usage to generate the error - FindCompileTimeConstant bogus_variable; - FindCompileTimeConstant bogus_variable2; + // Sample usage to generate the error + FindCompileTimeConstant bogus_variable; + FindCompileTimeConstant bogus_variable2; } #endif // defined(__cplusplus) && defined(USE_COMPILE_TIME_CONSTANT_FINDER) diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index b0fb6508d11ac0..983c396866f9f9 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1823,11 +1823,16 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) // If you modify this logic, please update DacDbiInterfaceImpl::GetDelegateType, DacDbiInterfaceImpl::GetDelegateType, // DacDbiInterfaceImpl::GetDelegateFunctionData, and DacDbiInterfaceImpl::GetDelegateTargetObject. - MethodDesc *pMethodHandle = NULL; - DELEGATEREF thisDel = (DELEGATEREF) orDelegate; DELEGATEREF innerDel = NULL; + MethodDesc *pMethodHandle = thisDel->GetMethodDesc(); + + if (pMethodHandle != NULL) + { + return pMethodHandle; + } + INT_PTR count = thisDel->GetInvocationCount(); if (count != 0) { @@ -1895,6 +1900,9 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) } _ASSERTE(pMethodHandle); + + thisDel->SetMethodDesc(pMethodHandle); + return pMethodHandle; } diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 4573109f584442..c08e606f7be353 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -245,15 +245,16 @@ DEFINE_METHOD(DECIMAL, CURRENCY_CTOR, .ctor, DEFINE_CLASS_U(System, Delegate, NoClass) DEFINE_FIELD_U(_target, DelegateObject, _target) -DEFINE_FIELD_U(_methodPtrAux, DelegateObject, _methodPtrAux) +DEFINE_FIELD_U(_methodDesc, DelegateObject, _methodDesc) DEFINE_FIELD_U(_methodPtr, DelegateObject, _methodPtr) +DEFINE_FIELD_U(_methodPtrAux, DelegateObject, _methodPtrAux) DEFINE_CLASS(DELEGATE, System, Delegate) DEFINE_FIELD(DELEGATE, TARGET, _target) -DEFINE_FIELD(DELEGATE, METHOD_PTR_AUX, _methodPtrAux) +DEFINE_FIELD(DELEGATE, METHOD_DESC, _methodDesc) DEFINE_FIELD(DELEGATE, METHOD_PTR, _methodPtr) +DEFINE_FIELD(DELEGATE, METHOD_PTR_AUX, _methodPtrAux) DEFINE_METHOD(DELEGATE, CONSTRUCT_DELEGATE, DelegateConstruct, IM_Obj_IntPtr_RetVoid) DEFINE_METHOD(DELEGATE, GET_INVOKE_METHOD, GetInvokeMethod, IM_RetIntPtr) -DEFINE_METHOD(DELEGATE, SET_CACHED_METHOD, SetCachedMethod, IM_Obj_RetVoid) DEFINE_CLASS(INT128, System, Int128) DEFINE_CLASS(UINT128, System, UInt128) diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index 7cf78bb87d0b47..c3841142a7ce4d 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -1877,7 +1877,7 @@ class DelegateObject : public Object MethodDesc* GetMethodDesc() { LIMITED_METHOD_CONTRACT; return _methodDesc; } void SetMethodDesc(MethodDesc* methodPtrAux) { LIMITED_METHOD_CONTRACT; _methodDesc = methodPtrAux; } - static int GetOffsetOfMethoddDesc() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodDesc); } + static int GetOffsetOfMethodDesc() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodDesc); } PCODE GetMethodPtr() { LIMITED_METHOD_CONTRACT; return _methodPtr; } void SetMethodPtr(PCODE methodPtr) { LIMITED_METHOD_CONTRACT; _methodPtr = methodPtr; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Delegate.cs b/src/libraries/System.Private.CoreLib/src/System/Delegate.cs index 951fab4c1e9125..a7133d3473a6c8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Delegate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Delegate.cs @@ -90,8 +90,8 @@ public abstract partial class Delegate : ICloneable, ISerializable /// The order of the delegates returned by the enumerator is the same order in which the current delegate invokes the methods that those delegates represent. /// The method returns an empty enumerator for null delegate. /// - public static Delegate.InvocationListEnumerator EnumerateInvocationList(TDelegate? d) where TDelegate : System.Delegate - => new Delegate.InvocationListEnumerator(Unsafe.As(d)); + public static System.Delegate.InvocationListEnumerator EnumerateInvocationList(TDelegate? d) where TDelegate : System.Delegate + => new InvocationListEnumerator(Unsafe.As(d)); /// /// Provides an enumerator for the invocation list of a delegate. @@ -135,7 +135,7 @@ public bool MoveNext() /// Implement IEnumerable.GetEnumerator() to return 'this' as the IEnumerator /// [EditorBrowsable(EditorBrowsableState.Never)] // Only here to make foreach work - public Delegate.InvocationListEnumerator GetEnumerator() => this; + public System.Delegate.InvocationListEnumerator GetEnumerator() => this; } public object? DynamicInvoke(params object?[]? args) From f3e48e15e5fbab81f068fbd225b7ef4ea6da61e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Thu, 17 Oct 2024 00:18:10 +0200 Subject: [PATCH 17/77] Add asserts --- src/coreclr/vm/comdelegate.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 983c396866f9f9..7f145926e6e292 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1293,6 +1293,7 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, LoaderAllocator *pLoaderAllocator = pTargetMethod->GetLoaderAllocator(); + _ASSERTE(refRealDelegate->GetInvocationList() == NULL); if (pLoaderAllocator->IsCollectible()) refRealDelegate->SetInvocationList(pLoaderAllocator->GetExposedObject()); @@ -1731,6 +1732,7 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q refThis->SetMethodDesc(pMethOrig); + _ASSERTE(refThis->GetInvocationList() == NULL); if (pMeth->GetLoaderAllocator()->IsCollectible()) refThis->SetInvocationList(pMeth->GetLoaderAllocator()->GetExposedObject()); From 870107807f2e1dc58e886cc08a6c8a717afb79a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Thu, 17 Oct 2024 19:43:54 +0200 Subject: [PATCH 18/77] Improve GetHashCode --- .../src/System/Delegate.CoreCLR.cs | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 1ede2f9579f335..1449aec9474b7c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -144,20 +144,12 @@ public override bool Equals([NotNullWhen(true)] object? obj) public override int GetHashCode() { - // - // this is not right in the face of a method being jitted in one delegate and not in another - // in that case the delegate is the same and Equals will return true but GetHashCode returns a - // different hashcode which is not true. - /* - if (_methodPtrAux == IntPtr.Zero) - return unchecked((int)((long)this._methodPtr)); - else - return unchecked((int)((long)this._methodPtrAux)); - */ - if (_methodPtrAux == IntPtr.Zero) - return (_target != null ? RuntimeHelpers.GetHashCode(_target) * 33 : 0) + GetType().GetHashCode(); - else - return GetType().GetHashCode(); + int hashCode = _methodDesc.GetHashCode(); + if (_methodPtrAux == IntPtr.Zero && _target != null) + { + hashCode += RuntimeHelpers.GetHashCode(_target) * 33; + } + return hashCode; } protected virtual MethodInfo GetMethodImpl() From 63cba0a5eea86b0ce915edc5f1cd10c61f623bbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Thu, 17 Oct 2024 21:12:43 +0200 Subject: [PATCH 19/77] Fix GetHashCode --- .../System.Private.CoreLib/src/System/Delegate.CoreCLR.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 1449aec9474b7c..97771f8c41b135 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -144,7 +144,7 @@ public override bool Equals([NotNullWhen(true)] object? obj) public override int GetHashCode() { - int hashCode = _methodDesc.GetHashCode(); + int hashCode = MethodDesc.GetHashCode(); if (_methodPtrAux == IntPtr.Zero && _target != null) { hashCode += RuntimeHelpers.GetHashCode(_target) * 33; From 10964cd76c8629639b334cf3e5116b2b45ab5ecd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Fri, 18 Oct 2024 03:10:16 +0200 Subject: [PATCH 20/77] Massage codegen --- .../src/System/Delegate.CoreCLR.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 97771f8c41b135..8771c9c29373d7 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -32,11 +32,10 @@ public abstract partial class Delegate : ICloneable, ISerializable internal IntPtr MethodDesc { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - if (_methodDesc == 0) - _methodDesc = GetMethodDesc(); - return _methodDesc; + return _methodDesc != 0 ? _methodDesc : GetMethodDesc(); } } @@ -537,10 +536,13 @@ internal static IRuntimeMethodInfo CreateMethodInfo(IntPtr methodDesc) [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_CreateMethodInfo")] private static partial void CreateMethodInfo(IntPtr methodDesc, ObjectHandleOnStack retMethodInfo); + [MethodImpl(MethodImplOptions.NoInlining)] private IntPtr GetMethodDesc() { Delegate d = this; - return GetMethodDesc(ObjectHandleOnStack.Create(ref d)); + IntPtr desc = GetMethodDesc(ObjectHandleOnStack.Create(ref d)); + _methodDesc = desc; + return desc; } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_GetMethodDesc")] From 26b6e61dea4222cffa56cd99d659623f2208b76a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Tue, 22 Oct 2024 21:00:30 +0200 Subject: [PATCH 21/77] Apply feedback --- .../System.Private.CoreLib/src/System/Delegate.CoreCLR.cs | 8 +++++++- .../src/System/MulticastDelegate.CoreCLR.cs | 4 ++-- src/coreclr/vm/comdelegate.cpp | 7 ------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 8771c9c29373d7..44ded20957b4d5 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -154,9 +154,15 @@ public override int GetHashCode() protected virtual MethodInfo GetMethodImpl() { return Cache.s_methodCache.TryGetValue(this, out MethodInfo? cachedValue) ? cachedValue : GetMethodImplUncached(); + + MethodInfo GetMethodImplUncached() + { + Debug.Assert(this is MulticastDelegate); + return Unsafe.As(this).GetMethodImplMulticast(); + } } - internal virtual MethodInfo GetMethodImplUncached() + internal MethodInfo GetMethodImplNormal() { IRuntimeMethodInfo method = CreateMethodInfo(MethodDesc); RuntimeType? declaringType = RuntimeMethodHandle.GetDeclaringType(method); diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index ffe5cffa0f4ca0..228fe411020106 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -494,7 +494,7 @@ public sealed override int GetHashCode() return base.GetTarget(); } - internal override MethodInfo GetMethodImplUncached() + internal MethodInfo GetMethodImplMulticast() { if (_invocationCount != 0 && _invocationList != null) { @@ -532,7 +532,7 @@ internal override MethodInfo GetMethodImplUncached() } // Otherwise, must be an inner delegate of a wrapper delegate of an open virtual method. In that case, call base implementation - return base.GetMethodImplUncached(); + return GetMethodImplNormal(); } // this should help inlining diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 7f145926e6e292..984cb7e0246c4f 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1204,8 +1204,6 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, pTargetMethod->EnsureActive(); - refRealDelegate->SetMethodDesc(pTargetMethod); - if (fIsOpenDelegate) { _ASSERTE(pRefFirstArg == NULL || *pRefFirstArg == NULL); @@ -1508,8 +1506,6 @@ OBJECTREF COMDelegate::ConvertToDelegate(LPVOID pCallback, MethodTable* pMT) // Wire up the unmanaged call stub to the delegate. delObj->SetTarget(delObj); // We are the "this" object - delObj->SetMethodDesc(pMD); - // For X86, we save the entry point in the delegate's method pointer and the UM Callsite in the aux pointer. delObj->SetMethodPtr(pMarshalStub); delObj->SetMethodPtrAux((PCODE)pCallback); @@ -1730,8 +1726,6 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q if (COMDelegate::NeedsWrapperDelegate(pMeth)) refThis = COMDelegate::CreateWrapperDelegate(refThis, pMeth); - refThis->SetMethodDesc(pMethOrig); - _ASSERTE(refThis->GetInvocationList() == NULL); if (pMeth->GetLoaderAllocator()->IsCollectible()) refThis->SetInvocationList(pMeth->GetLoaderAllocator()->GetExposedObject()); @@ -2149,7 +2143,6 @@ DELEGATEREF COMDelegate::CreateWrapperDelegate(DELEGATEREF delegate, MethodDesc* // save the secure invoke stub. GetWrapperInvoke() can trigger GC. PCODE tmp = GetWrapperInvoke(pMD); - gc.refWrapperDel->SetMethodDesc(pMD); gc.refWrapperDel->SetMethodPtr(tmp); // save the delegate MethodDesc for the frame gc.refWrapperDel->SetInvocationCount((INT_PTR)pMD); From 99e37d61266c89784ff7fc70d011104b0c2b5d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Thu, 24 Oct 2024 20:09:48 +0200 Subject: [PATCH 22/77] Overload invocationList for DynamicMethods --- .../src/System/MulticastDelegate.CoreCLR.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index 228fe411020106..bc2ee9e995aeb9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -19,6 +19,7 @@ public abstract partial class MulticastDelegate : Delegate // This is set under 2 circumstances // 1. Multicast delegate // 2. Wrapper delegate + // 3. DynamicMethods private object? _invocationList; // Initialized by VM as needed private nint _invocationCount; @@ -29,7 +30,7 @@ internal bool IsUnmanagedFunctionPtr() internal bool InvocationListLogicallyNull() { - return (_invocationList == null) || (_invocationList is LoaderAllocator) || (_invocationList is DynamicResolver); + return (_invocationList == null) || (_invocationList is LoaderAllocator) || (_invocationList is DynamicResolver) || (_invocationList is DynamicMethod); } [Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] @@ -200,10 +201,13 @@ internal void StoreDynamicMethod(MethodInfo dynamicMethod) Debug.Assert(!IsUnmanagedFunctionPtr(), "dynamic method and unmanaged fntptr delegate combined"); // must be a secure/wrapper one, unwrap and save MulticastDelegate d = ((MulticastDelegate?)_invocationList)!; - d.SetCachedMethod(dynamicMethod); + d.StoreDynamicMethod(dynamicMethod); } else - SetCachedMethod(dynamicMethod); + { + Debug.Assert(InvocationListLogicallyNull(), "dynamic method with invocation list"); + _invocationList = dynamicMethod; + } } // This method will combine this delegate with the passed delegate @@ -511,6 +515,10 @@ internal MethodInfo GetMethodImplMulticast() return innerDelegate.GetMethodImpl(); } } + else if (_invocationList is DynamicMethod dynamicMethod) + { + return dynamicMethod; + } else if (IsUnmanagedFunctionPtr()) { // we handle unmanaged function pointers here because the generic ones (used for WinRT) would otherwise From b4075c4c546f12a21e5e4635b5ce5fd668cfc723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sun, 11 May 2025 01:58:17 +0200 Subject: [PATCH 23/77] Remove newline diff --- src/coreclr/vm/comdelegate.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index b4a7a252a5264a..c1378f1159fed0 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -2103,7 +2103,6 @@ DELEGATEREF COMDelegate::CreateWrapperDelegate(DELEGATEREF delegate, MethodDesc* // save the secure invoke stub. GetWrapperInvoke() can trigger GC. PCODE tmp = GetWrapperInvoke(pMD); - gc.refWrapperDel->SetMethodPtr(tmp); // save the delegate MethodDesc for the frame gc.refWrapperDel->SetInvocationCount((INT_PTR)pMD); From 405309f3bcc1c3e82272c5bf695ea29d51086c62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sun, 11 May 2025 01:59:20 +0200 Subject: [PATCH 24/77] Update comdelegate.h From 6caeb42f1406a48a6323d120c20b5976746f6be1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sun, 22 Mar 2026 01:23:19 +0100 Subject: [PATCH 25/77] Update src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs Co-authored-by: Sergio Pedri --- .../src/System/MulticastDelegate.CoreCLR.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index 4c7fb1299a37da..98de31558bc88c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -17,7 +17,7 @@ namespace System [ComVisible(true)] public abstract partial class MulticastDelegate : Delegate { - // This is set under 2 circumstances + // This is set under 3 circumstances // 1. Multicast delegate // 2. Wrapper delegate // 3. DynamicMethods From 43c2c820505b43d7731e89f82ae3554a20d55e9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Fri, 24 Apr 2026 19:41:21 +0200 Subject: [PATCH 26/77] Cleanup --- .../src/System/Delegate.CoreCLR.cs | 19 ++++++++----------- .../src/System/MulticastDelegate.CoreCLR.cs | 10 ++++------ 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 72da04f675afab..8711e806022baa 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -155,14 +155,19 @@ protected virtual MethodInfo GetMethodImpl() { return Cache.s_methodCache.TryGetValue(this, out MethodInfo? cachedValue) ? cachedValue : GetMethodImplUncached(); + [MethodImpl(MethodImplOptions.NoInlining)] MethodInfo GetMethodImplUncached() { Debug.Assert(this is MulticastDelegate); - return Unsafe.As(this).GetMethodImplMulticast(); + MethodInfo method = Unsafe.As(this).GetMethodImplMulticast(); + + Debug.Assert(method is not null); + Cache.s_methodCache.AddOrUpdate(this, method); + return method; } } - internal MethodInfo GetMethodImplNormal() + internal MethodInfo GetMethodImplSimple() { IRuntimeMethodInfo method = CreateMethodInfo(MethodDesc); RuntimeType? declaringType = RuntimeMethodHandle.GetDeclaringType(method); @@ -214,15 +219,7 @@ internal MethodInfo GetMethodImplNormal() } } } - MethodInfo methodInfo = (MethodInfo)RuntimeType.GetMethodBase(declaringType, method)!; - SetCachedMethod(methodInfo); - return methodInfo; - } - - internal void SetCachedMethod(MethodInfo methodInfo) - { - Debug.Assert(methodInfo is not null); - Cache.s_methodCache.AddOrUpdate(this, methodInfo); + return (MethodInfo)RuntimeType.GetMethodBase(declaringType, method)!; } public object? Target => GetTarget(); diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index 98de31558bc88c..95bc7cde0d2a45 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -31,7 +31,7 @@ internal bool IsUnmanagedFunctionPtr() internal bool InvocationListLogicallyNull() { - return (_invocationList == null) || (_invocationList is LoaderAllocator) || (_invocationList is DynamicResolver) || (_invocationList is DynamicMethod); + return _invocationList is null or LoaderAllocator or DynamicResolver or DynamicMethod; } [Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] @@ -535,13 +535,11 @@ internal MethodInfo GetMethodImplMulticast() declaringType = reflectedType; } - MethodInfo methodInfo = (MethodInfo)RuntimeType.GetMethodBase(declaringType, method)!; - SetCachedMethod(methodInfo); - return methodInfo; + return (MethodInfo)RuntimeType.GetMethodBase(declaringType, method)!; } - // Otherwise, must be an inner delegate of a wrapper delegate of an open virtual method. In that case, call base implementation - return GetMethodImplNormal(); + // Otherwise, must be a non multicast delegate. + return GetMethodImplSimple(); } // this should help inlining From e0a52e30dec42282537f9037db476e1a90a76d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Wed, 10 Jun 2026 11:20:03 +0200 Subject: [PATCH 27/77] Address review --- .../src/System/MulticastDelegate.CoreCLR.cs | 3 ++- src/coreclr/vm/comdelegate.cpp | 4 ++-- src/coreclr/vm/corelib.h | 1 - src/coreclr/vm/object.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index 95bc7cde0d2a45..81347180699bd5 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -17,10 +17,11 @@ namespace System [ComVisible(true)] public abstract partial class MulticastDelegate : Delegate { - // This is set under 3 circumstances + // This is set under 4 circumstances // 1. Multicast delegate // 2. Wrapper delegate // 3. DynamicMethods + // 4. Collectible delegates private object? _invocationList; // Initialized by VM as needed private nint _invocationCount; diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 4475b2fb8e17d7..24445074500754 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -2798,7 +2798,7 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT BOOL isCollectible = pTargetMethodLoaderAllocator->IsCollectible(); // A method that may be instantiated over a collectible type, and is static will require a delegate // that has the LoaderAllocator of the collectible assembly associated with the instantiation - // stored in the MethodInfo cache. + // stored in the _invocationList field. BOOL fMaybeCollectibleAndStatic = FALSE; if (isStatic) @@ -2882,7 +2882,7 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT // // 7 - Needs special handling // - // With collectible types, we need to fill the _methodBase field in with a value that represents the LoaderAllocator of the target method + // With collectible types, we need to fill the _invocationList field in with a value that represents the LoaderAllocator of the target method // if the delegate is not a closed instance delegate. // // There are two techniques that will work for this. diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index c53bdacea9f649..b32a2ad83fd124 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -234,7 +234,6 @@ DEFINE_FIELD_U(_methodPtr, DelegateObject, _methodPtr) DEFINE_FIELD_U(_methodPtrAux, DelegateObject, _methodPtrAux) DEFINE_CLASS(DELEGATE, System, Delegate) DEFINE_FIELD(DELEGATE, TARGET, _target) -DEFINE_FIELD(DELEGATE, METHOD_DESC, _methodDesc) DEFINE_FIELD(DELEGATE, METHOD_PTR, _methodPtr) DEFINE_FIELD(DELEGATE, METHOD_PTR_AUX, _methodPtrAux) DEFINE_METHOD(DELEGATE, CONSTRUCT_DELEGATE, DelegateConstruct, IM_Obj_IntPtr_RetVoid) diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index 1edcfc1a1092bf..4ae526cd869c21 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -1759,7 +1759,7 @@ class DelegateObject : public Object static int GetOffsetOfTarget() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _target); } MethodDesc* GetMethodDesc() { LIMITED_METHOD_CONTRACT; return _methodDesc; } - void SetMethodDesc(MethodDesc* methodPtrAux) { LIMITED_METHOD_CONTRACT; _methodDesc = methodPtrAux; } + void SetMethodDesc(MethodDesc* methodDesc) { LIMITED_METHOD_CONTRACT; _methodDesc = methodDesc; } static int GetOffsetOfMethodDesc() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodDesc); } PCODE GetMethodPtr() { LIMITED_METHOD_CONTRACT; return _methodPtr; } From 9c7c07d4731f22c962960323b9ee40388ea43041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Wed, 17 Jun 2026 03:10:30 +0200 Subject: [PATCH 28/77] Remove CWT, cleanup delegate impl --- .../src/System/Delegate.CoreCLR.cs | 189 +- .../src/System/Delegate.CoreCLR.cs.bak | 592 ++++ .../src/System/Delegate.CoreCLR.cs.orig | 592 ++++ .../src/System/MulticastDelegate.CoreCLR.cs | 534 ++-- src/coreclr/debug/daccess/dacdbiimpl.cpp | 8 +- src/coreclr/debug/ee/controller.cpp | 2 +- src/coreclr/jit/flowgraph.cpp | 6 +- .../tools/Common/JitInterface/CorInfoImpl.cs | 4 +- src/coreclr/vm/amd64/asmconstants.h | 6 +- src/coreclr/vm/arm/stubs.cpp | 4 +- src/coreclr/vm/arm64/stubs.cpp | 6 +- src/coreclr/vm/comdelegate.cpp | 117 +- src/coreclr/vm/corelib.h | 31 +- src/coreclr/vm/dllimport.cpp | 4 +- src/coreclr/vm/dllimportcallback.h | 2 +- src/coreclr/vm/i386/stublinkerx86.cpp | 8 +- src/coreclr/vm/i386/virtualcallstubcpu.hpp | 2 +- src/coreclr/vm/interpexec.cpp | 6 +- src/coreclr/vm/jithelpers.cpp | 8 +- src/coreclr/vm/jitinterface.cpp | 14 +- src/coreclr/vm/loongarch64/stubs.cpp | 6 +- src/coreclr/vm/object.h | 64 +- src/coreclr/vm/object.h.bak | 2586 +++++++++++++++++ src/coreclr/vm/object.h.orig | 2586 +++++++++++++++++ src/coreclr/vm/riscv64/stubs.cpp | 4 +- src/coreclr/vm/stubhelpers.cpp | 4 +- src/coreclr/vm/stubmgr.cpp | 12 +- src/coreclr/vm/virtualcallstub.cpp | 2 +- .../System/Runtime/CompilerServices/Unsafe.cs | 9 + 29 files changed, 6855 insertions(+), 553 deletions(-) create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs.bak create mode 100644 src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs.orig create mode 100644 src/coreclr/vm/object.h.bak create mode 100644 src/coreclr/vm/object.h.orig diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 7498ce1fec067c..0cc5ee6bde1877 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -4,37 +4,47 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; +using System.Runtime.Versioning; +using System.Threading; namespace System { [ClassInterface(ClassInterfaceType.None)] [ComVisible(true)] + [NonVersionable] public abstract partial class Delegate : ICloneable, ISerializable { - // MethodBase, either cached after first request or assigned from a DynamicMethod - // For open delegates to collectible types, this may be a LoaderAllocator object - internal object? _methodBase; - - // _target is the object we will invoke on; null if static delegate - internal object? _target; // Keep _target and _methodPtr next to each other for optimal delegate invoke performance - - // _methodPtr is a pointer to the method we will invoke + // This is set under 4 circumstances + // 1. Multicast delegate + // 2. Wrapper delegate + // 3. Method cache + // 4. Collectible delegates + internal object? _helperObject; // Initialized by VM as needed + + // _firstParameter is the object we will invoke on; null if static delegate + // Keep _firstParameter and _functionPointer next to each other for optimal delegate invoke performance + internal object? _firstParameter; + + // _functionPointer is a pointer to the method we will invoke // It could be a small thunk if this is a static or UM call - internal IntPtr _methodPtr; + internal nuint _functionPointer; // In the case of a static method passed to a delegate, this field stores - // whatever _methodPtr would have stored: and _methodPtr points to a + // whatever _functionPointer would have stored: and _functionPointer points to a // small thunk which removes the "this" pointer before going on - // to _methodPtrAux. - internal IntPtr _methodPtrAux; + // to _functionPointer. + internal nint _extraFunctionPointerOrData; + + internal nint _invocationCount; // VM handle to wrapped method, null when not initialized - private IntPtr _methodDesc; + private nuint _methodDesc; - internal IntPtr MethodDesc + private nuint MethodDesc { [MethodImpl(MethodImplOptions.AggressiveInlining)] get @@ -43,6 +53,38 @@ internal IntPtr MethodDesc } } + internal bool IsSpecialDelegate => _invocationCount != 0; + + internal bool IsUnmanagedFunctionPtr => _invocationCount == -1; + + internal bool InvocationListLogicallyNull => _helperObject is not (object[] or Delegate); + + private object[] GetObjectArray() + { + Debug.Assert(IsSpecialDelegate); + + object[]? invocationList = Unsafe.AsAssert(_helperObject); + + Debug.Assert(invocationList is not null); + Debug.Assert(invocationList.Length > 1); + Debug.Assert(invocationList[0] is MulticastDelegate); + + Debug.Assert((uint)invocationList.Length >= (nuint)_invocationCount); + return invocationList; + } + + internal ReadOnlySpan GetInvocationsUnchecked() + { + ref MulticastDelegate first = ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(GetObjectArray())); + return MemoryMarshal.CreateReadOnlySpan(ref first, (int)_invocationCount); + } + + internal MulticastDelegate GetLastInvocationUnchecked() + { + object last = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(GetObjectArray()), (nuint)_invocationCount - 1); + return Unsafe.AsAssert(last); + } + // This constructor is called from the class generated by the // compiler generated code [RequiresUnreferencedCode("The target method might be removed")] @@ -92,7 +134,7 @@ protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Al protected virtual object? DynamicInvokeImpl(object?[]? args) { RuntimeMethodHandleInternal method = new RuntimeMethodHandleInternal(GetInvokeMethod()); - RuntimeMethodInfo invoke = (RuntimeMethodInfo)RuntimeType.GetMethodBase((RuntimeType)this.GetType(), method)!; + RuntimeMethodInfo invoke = (RuntimeMethodInfo)RuntimeType.GetMethodBase((RuntimeType)GetType(), method)!; return invoke.Invoke(this, BindingFlags.Default, null, args, null); } @@ -102,10 +144,12 @@ public override bool Equals([NotNullWhen(true)] object? obj) if (obj == null || !InternalEqualTypes(this, obj)) return false; - Delegate d = (Delegate)obj; + Delegate other = Unsafe.AsAssert(obj); // do an optimistic check first. This is hopefully cheap enough to be worth - if (_target == d._target && _methodPtr == d._methodPtr && _methodPtrAux == d._methodPtrAux) + if (_firstParameter == other._firstParameter && + _functionPointer == other._functionPointer && + _extraFunctionPointerOrData == other._extraFunctionPointerOrData) return true; // even though the fields were not all equals the delegates may still match @@ -114,75 +158,99 @@ public override bool Equals([NotNullWhen(true)] object? obj) // It may also happen that the method pointer was not jitted when creating one delegate and jitted in the other // if that's the case the delegates may still be equals but we need to make a more complicated check - if (_methodPtrAux == IntPtr.Zero) + if (_extraFunctionPointerOrData == 0) { - if (d._methodPtrAux != IntPtr.Zero) + if (other._extraFunctionPointerOrData != 0) return false; // different delegate kind // they are both closed over the first arg - if (_target != d._target) + if (_firstParameter != other._firstParameter) return false; // fall through method handle check } else { - if (d._methodPtrAux == IntPtr.Zero) + if (other._extraFunctionPointerOrData == 0) return false; // different delegate kind // Ignore the target as it will be the delegate instance, though it may be a different one - /* - if (_methodPtr != d._methodPtr) - return false; - */ - if (_methodPtrAux == d._methodPtrAux) + if (_extraFunctionPointerOrData == other._extraFunctionPointerOrData) return true; // fall through method handle check } - return MethodDesc == d.MethodDesc; + return MethodDesc == other.MethodDesc; } public override int GetHashCode() { int hashCode = MethodDesc.GetHashCode(); - if (_methodPtrAux == IntPtr.Zero && _target != null) + if (_extraFunctionPointerOrData == 0 && _firstParameter != null) { - hashCode += RuntimeHelpers.GetHashCode(_target) * 33; + hashCode += RuntimeHelpers.GetHashCode(_firstParameter) * 33; } return hashCode; } + internal virtual object? GetTarget() + { + return _extraFunctionPointerOrData == 0 ? _firstParameter : null; + } + protected virtual MethodInfo GetMethodImpl() { - return Cache.s_methodCache.TryGetValue(this, out MethodInfo? cachedValue) ? cachedValue : GetMethodImplUncached(); + return _helperObject switch + { + MethodInfo methodInfo => methodInfo, + object[] => GetLastInvocationUnchecked().Method, + Delegate wrapper => wrapper.Method, + _ => GetMethodImplUncached() + }; [MethodImpl(MethodImplOptions.NoInlining)] MethodInfo GetMethodImplUncached() { - Debug.Assert(this is MulticastDelegate); - MethodInfo method = Unsafe.As(this).GetMethodImplMulticast(); + Debug.Assert(InvocationListLogicallyNull); + MethodInfo method = GetMethodImplCore(); Debug.Assert(method is not null); - Cache.s_methodCache.AddOrUpdate(this, method); + + Volatile.Write(ref _helperObject, method); return method; } } - internal MethodInfo GetMethodImplSimple() + private MethodInfo GetMethodImplCore() { + // should be handled by GetMethodImpl + Debug.Assert(InvocationListLogicallyNull); + IRuntimeMethodInfo method = CreateMethodInfo(MethodDesc); RuntimeType? declaringType = RuntimeMethodHandle.GetDeclaringType(method); + if (IsUnmanagedFunctionPtr) + { + // we handle unmanaged function pointers here because the generic ones (used for WinRT) would otherwise + // be treated as open delegates, resulting in failure to get the MethodInfo + + // need a proper declaring type instance method on a generic type + if (declaringType.IsGenericType) + { + // we are returning the 'Invoke' method of this delegate so use this.GetType() for the exact type + RuntimeType reflectedType = (RuntimeType)GetType(); + declaringType = reflectedType; + } + } // need a proper declaring type instance method on a generic type - if (declaringType.IsGenericType) + else if (declaringType.IsGenericType) { - bool isStatic = (RuntimeMethodHandle.GetAttributes(method) & MethodAttributes.Static) != (MethodAttributes)0; + bool isStatic = (RuntimeMethodHandle.GetAttributes(method) & MethodAttributes.Static) != 0; if (!isStatic) { - if (_methodPtrAux == IntPtr.Zero) + if (_extraFunctionPointerOrData == 0) { // The target may be of a derived type that doesn't have visibility onto the // target method. We don't want to call RuntimeType.GetMethodBase below with that @@ -196,7 +264,7 @@ internal MethodInfo GetMethodImplSimple() // walking won't be we compare using the generic type definition forms instead. Type targetType = declaringType.GetGenericTypeDefinition(); Type? currentType; - for (currentType = _target!.GetType(); currentType != null; currentType = currentType.BaseType) + for (currentType = _firstParameter!.GetType(); currentType != null; currentType = currentType.BaseType) { if (currentType.IsGenericType && currentType.GetGenericTypeDefinition() == targetType) @@ -212,13 +280,13 @@ internal MethodInfo GetMethodImplSimple() // The targetType may also be an interface with a Default interface method (DIM). Debug.Assert( currentType != null - || _target.GetType().IsCOMObject + || _firstParameter.GetType().IsCOMObject || targetType.IsInterface, "The class hierarchy should declare the method or be a DIM"); } else { // it's an open one, need to fetch the first arg of the instantiation - MethodInfo invoke = this.GetType().GetMethod("Invoke")!; + MethodInfo invoke = GetType().GetMethod("Invoke")!; declaringType = (RuntimeType)invoke.GetParametersAsSpan()[0].ParameterType; } } @@ -226,6 +294,22 @@ internal MethodInfo GetMethodImplSimple() return (MethodInfo)RuntimeType.GetMethodBase(declaringType, method)!; } + internal void StoreDynamicMethod(MethodInfo dynamicMethod) + { + if (IsSpecialDelegate) + { + Debug.Assert(!IsUnmanagedFunctionPtr, "dynamic method and unmanaged fntptr delegate combined"); + + // must be a secure/wrapper one, unwrap and save + Delegate d = ((Delegate?)_helperObject)!; + d.StoreDynamicMethod(dynamicMethod); + return; + } + + Debug.Assert(InvocationListLogicallyNull, "dynamic method with invocation list"); + _helperObject = dynamicMethod; + } + public object? Target => GetTarget(); // V1 API. @@ -496,7 +580,7 @@ private void DelegateConstruct(object target, IntPtr method) [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_GetMulticastInvokeSlow")] private static unsafe partial void* GetMulticastInvokeSlow(MethodTable* pMT); - internal unsafe IntPtr GetMulticastInvoke() + internal unsafe nuint GetMulticastInvoke() { MethodTable* pMT = RuntimeHelpers.GetMethodTable(this); void* ptr = GetMulticastInvoke(pMT); @@ -507,7 +591,7 @@ internal unsafe IntPtr GetMulticastInvoke() Debug.Assert(ptr == GetMulticastInvoke(pMT)); } // No GC.KeepAlive() since the caller must keep instance alive to use returned pointer. - return (IntPtr)ptr; + return (nuint)ptr; } [MethodImpl(MethodImplOptions.InternalCall)] @@ -521,7 +605,7 @@ internal unsafe IntPtr GetInvokeMethod() return (IntPtr)ptr; } - internal static IRuntimeMethodInfo CreateMethodInfo(IntPtr methodDesc) + internal static IRuntimeMethodInfo CreateMethodInfo(nuint methodDesc) { IRuntimeMethodInfo? methodInfo = null; CreateMethodInfo(methodDesc, ObjectHandleOnStack.Create(ref methodInfo)); @@ -529,19 +613,19 @@ internal static IRuntimeMethodInfo CreateMethodInfo(IntPtr methodDesc) } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_CreateMethodInfo")] - private static partial void CreateMethodInfo(IntPtr methodDesc, ObjectHandleOnStack retMethodInfo); + private static partial void CreateMethodInfo(nuint methodDesc, ObjectHandleOnStack retMethodInfo); [MethodImpl(MethodImplOptions.NoInlining)] - private IntPtr GetMethodDesc() + private nuint GetMethodDesc() { Delegate d = this; - IntPtr desc = GetMethodDesc(ObjectHandleOnStack.Create(ref d)); + nuint desc = GetMethodDesc(ObjectHandleOnStack.Create(ref d)); _methodDesc = desc; return desc; } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_GetMethodDesc")] - private static partial IntPtr GetMethodDesc(ObjectHandleOnStack instance); + private static partial nuint GetMethodDesc(ObjectHandleOnStack instance); internal static IntPtr AdjustTarget(object target, IntPtr methodPtr) { @@ -559,17 +643,6 @@ internal void InitializeVirtualCallStub(IntPtr methodPtr) [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_InitializeVirtualCallStub")] private static partial void InitializeVirtualCallStub(ObjectHandleOnStack d, IntPtr methodPtr); - - internal virtual object? GetTarget() - { - return (_methodPtrAux == IntPtr.Zero) ? _target : null; - } - - // Caches MethodInfos, added either after first request or assigned from a DynamicMethod - private static class Cache - { - public static readonly ConditionalWeakTable s_methodCache = new(); - } } // These flags effect the way BindToMethodInfo and BindToMethodName are allowed to bind a delegate to a target method. Their diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs.bak b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs.bak new file mode 100644 index 00000000000000..5319d8a8d2f6a0 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs.bak @@ -0,0 +1,592 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace System +{ + [ClassInterface(ClassInterfaceType.None)] + [ComVisible(true)] + public abstract partial class Delegate : ICloneable, ISerializable + { +<<<<<<< HEAD + // _target is the object we will invoke on + internal object? _target; // Initialized by VM as needed; null if static delegate + + // VM handle to wrapped method, null when not initialized + private IntPtr _methodDesc; +======= + // MethodBase, either cached after first request or assigned from a DynamicMethod + // For open delegates to collectible types, this may be a LoaderAllocator object + internal object? _methodBase; + + // _target is the object we will invoke on; null if static delegate + internal object? _target; // Keep _target and _methodPtr next to each other for optimal delegate invoke performance +>>>>>>> upstream/main + + // _methodPtr is a pointer to the method we will invoke + // It could be a small thunk if this is a static or UM call + internal IntPtr _methodPtr; + + // In the case of a static method passed to a delegate, this field stores + // whatever _methodPtr would have stored: and _methodPtr points to a + // small thunk which removes the "this" pointer before going on + // to _methodPtrAux. + internal IntPtr _methodPtrAux; + + internal IntPtr MethodDesc + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return _methodDesc != 0 ? _methodDesc : GetMethodDesc(); + } + } + + // This constructor is called from the class generated by the + // compiler generated code + [RequiresUnreferencedCode("The target method might be removed")] + protected Delegate(object target, string method) + { + ArgumentNullException.ThrowIfNull(target); + ArgumentNullException.ThrowIfNull(method); + + // This API existed in v1/v1.1 and only expected to create closed + // instance delegates. Constrain the call to BindToMethodName to + // such and don't allow relaxed signature matching (which could make + // the choice of target method ambiguous) for backwards + // compatibility. The name matching was case sensitive and we + // preserve that as well. + if (!BindToMethodName(target, (RuntimeType)target.GetType(), method, + DelegateBindingFlags.InstanceMethodOnly | + DelegateBindingFlags.ClosedDelegateOnly)) + throw new ArgumentException(SR.Arg_DlgtTargMeth); + } + + // This constructor is called from a class to generate a + // delegate based upon a static method name and the Type object + // for the class defining the method. + protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllMethods)] Type target, string method) + { + ArgumentNullException.ThrowIfNull(target); + ArgumentNullException.ThrowIfNull(method); + + if (target.ContainsGenericParameters) + throw new ArgumentException(SR.Arg_UnboundGenParam, nameof(target)); + if (target is not RuntimeType rtTarget) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(target)); + + // This API existed in v1/v1.1 and only expected to create open + // static delegates. Constrain the call to BindToMethodName to such + // and don't allow relaxed signature matching (which could make the + // choice of target method ambiguous) for backwards compatibility. + // The name matching was case insensitive (no idea why this is + // different from the constructor above) and we preserve that as + // well. + BindToMethodName(null, rtTarget, method, + DelegateBindingFlags.StaticMethodOnly | + DelegateBindingFlags.OpenDelegateOnly | + DelegateBindingFlags.CaselessMatching); + } + + protected virtual object? DynamicInvokeImpl(object?[]? args) + { + RuntimeMethodHandleInternal method = new RuntimeMethodHandleInternal(GetInvokeMethod()); + RuntimeMethodInfo invoke = (RuntimeMethodInfo)RuntimeType.GetMethodBase((RuntimeType)this.GetType(), method)!; + + return invoke.Invoke(this, BindingFlags.Default, null, args, null); + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj == null || !InternalEqualTypes(this, obj)) + return false; + + Delegate d = (Delegate)obj; + + // do an optimistic check first. This is hopefully cheap enough to be worth + if (_target == d._target && _methodPtr == d._methodPtr && _methodPtrAux == d._methodPtrAux) + return true; + + // even though the fields were not all equals the delegates may still match + // When target carries the delegate itself the 2 targets (delegates) may be different instances + // but the delegates are logically the same + // It may also happen that the method pointer was not jitted when creating one delegate and jitted in the other + // if that's the case the delegates may still be equals but we need to make a more complicated check + + if (_methodPtrAux == IntPtr.Zero) + { + if (d._methodPtrAux != IntPtr.Zero) + return false; // different delegate kind + + // they are both closed over the first arg + if (_target != d._target) + return false; + + // fall through method handle check + } + else + { + if (d._methodPtrAux == IntPtr.Zero) + return false; // different delegate kind + + // Ignore the target as it will be the delegate instance, though it may be a different one + /* + if (_methodPtr != d._methodPtr) + return false; + */ + + if (_methodPtrAux == d._methodPtrAux) + return true; + + // fall through method handle check + } + + return MethodDesc == d.MethodDesc; + } + + public override int GetHashCode() + { + int hashCode = MethodDesc.GetHashCode(); + if (_methodPtrAux == IntPtr.Zero && _target != null) + { + hashCode += RuntimeHelpers.GetHashCode(_target) * 33; + } + return hashCode; + } + + protected virtual MethodInfo GetMethodImpl() + { + return Cache.s_methodCache.TryGetValue(this, out MethodInfo? cachedValue) ? cachedValue : GetMethodImplUncached(); + + [MethodImpl(MethodImplOptions.NoInlining)] + MethodInfo GetMethodImplUncached() + { + Debug.Assert(this is MulticastDelegate); + MethodInfo method = Unsafe.As(this).GetMethodImplMulticast(); + + Debug.Assert(method is not null); + Cache.s_methodCache.AddOrUpdate(this, method); + return method; + } + } + + internal MethodInfo GetMethodImplSimple() + { + IRuntimeMethodInfo method = CreateMethodInfo(MethodDesc); + RuntimeType? declaringType = RuntimeMethodHandle.GetDeclaringType(method); + + // need a proper declaring type instance method on a generic type + if (declaringType.IsGenericType) + { + bool isStatic = (RuntimeMethodHandle.GetAttributes(method) & MethodAttributes.Static) != (MethodAttributes)0; + if (!isStatic) + { + if (_methodPtrAux == IntPtr.Zero) + { + // The target may be of a derived type that doesn't have visibility onto the + // target method. We don't want to call RuntimeType.GetMethodBase below with that + // or reflection can end up generating a MethodInfo where the ReflectedType cannot + // see the MethodInfo itself and that breaks an important invariant. But the + // target type could include important generic type information we need in order + // to work out what the exact instantiation of the method's declaring type is. So + // we'll walk up the inheritance chain (which will yield exactly instantiated + // types at each step) until we find the declaring type. Since the declaring type + // we get from the method is probably shared and those in the hierarchy we're + // walking won't be we compare using the generic type definition forms instead. + Type targetType = declaringType.GetGenericTypeDefinition(); + Type? currentType; + for (currentType = _target!.GetType(); currentType != null; currentType = currentType.BaseType) + { + if (currentType.IsGenericType && + currentType.GetGenericTypeDefinition() == targetType) + { + declaringType = currentType as RuntimeType; + break; + } + } + + // RCWs don't need to be "strongly-typed" in which case we don't find a base type + // that matches the declaring type of the method. This is fine because interop needs + // to work with exact methods anyway so declaringType is never shared at this point. + // The targetType may also be an interface with a Default interface method (DIM). + Debug.Assert( + currentType != null + || _target.GetType().IsCOMObject + || targetType.IsInterface, "The class hierarchy should declare the method or be a DIM"); + } + else + { + // it's an open one, need to fetch the first arg of the instantiation + MethodInfo invoke = this.GetType().GetMethod("Invoke")!; + declaringType = (RuntimeType)invoke.GetParametersAsSpan()[0].ParameterType; + } + } + } + return (MethodInfo)RuntimeType.GetMethodBase(declaringType, method)!; + } + + public object? Target => GetTarget(); + + // V1 API. + [RequiresUnreferencedCode("The target method might be removed")] + public static Delegate? CreateDelegate(Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure) + { + ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(target); + ArgumentNullException.ThrowIfNull(method); + + if (type is not RuntimeType rtType) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); + if (!rtType.IsDelegate()) + throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); + + Delegate d = InternalAlloc(rtType); + // This API existed in v1/v1.1 and only expected to create closed + // instance delegates. Constrain the call to BindToMethodName to such + // and don't allow relaxed signature matching (which could make the + // choice of target method ambiguous) for backwards compatibility. + // We never generate a closed over null delegate and this is + // actually enforced via the check on target above, but we pass + // NeverCloseOverNull anyway just for clarity. + if (!d.BindToMethodName(target, (RuntimeType)target.GetType(), method, + DelegateBindingFlags.InstanceMethodOnly | + DelegateBindingFlags.ClosedDelegateOnly | + DelegateBindingFlags.NeverCloseOverNull | + (ignoreCase ? DelegateBindingFlags.CaselessMatching : 0))) + { + if (throwOnBindFailure) + throw new ArgumentException(SR.Arg_DlgtTargMeth); + + return null; + } + + return d; + } + + // V1 API. + public static Delegate? CreateDelegate(Type type, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllMethods)] Type target, string method, bool ignoreCase, bool throwOnBindFailure) + { + ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(target); + ArgumentNullException.ThrowIfNull(method); + + if (target.ContainsGenericParameters) + throw new ArgumentException(SR.Arg_UnboundGenParam, nameof(target)); + if (type is not RuntimeType rtType) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); + if (target is not RuntimeType rtTarget) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(target)); + + if (!rtType.IsDelegate()) + throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); + + Delegate d = InternalAlloc(rtType); + // This API existed in v1/v1.1 and only expected to create open + // static delegates. Constrain the call to BindToMethodName to such + // and don't allow relaxed signature matching (which could make the + // choice of target method ambiguous) for backwards compatibility. + if (!d.BindToMethodName(null, rtTarget, method, + DelegateBindingFlags.StaticMethodOnly | + DelegateBindingFlags.OpenDelegateOnly | + (ignoreCase ? DelegateBindingFlags.CaselessMatching : 0))) + { + if (throwOnBindFailure) + throw new ArgumentException(SR.Arg_DlgtTargMeth); + + return null; + } + + return d; + } + + // V1 API. + public static Delegate? CreateDelegate(Type type, MethodInfo method, bool throwOnBindFailure) + { + ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(method); + + if (type is not RuntimeType rtType) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); + + if (method is not RuntimeMethodInfo rmi) + throw new ArgumentException(SR.Argument_MustBeRuntimeMethodInfo, nameof(method)); + + if (!rtType.IsDelegate()) + throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); + + // This API existed in v1/v1.1 and only expected to create closed + // instance delegates. Constrain the call to BindToMethodInfo to + // open delegates only for backwards compatibility. But we'll allow + // relaxed signature checking and open static delegates because + // there's no ambiguity there (the caller would have to explicitly + // pass us a static method or a method with a non-exact signature + // and the only change in behavior from v1.1 there is that we won't + // fail the call). + Delegate? d = CreateDelegateInternal( + rtType, + rmi, + null, + DelegateBindingFlags.OpenDelegateOnly | DelegateBindingFlags.RelaxedSignature); + + if (d == null && throwOnBindFailure) + throw new ArgumentException(SR.Arg_DlgtTargMeth); + + return d; + } + + // V2 API. + public static Delegate? CreateDelegate(Type type, object? firstArgument, MethodInfo method, bool throwOnBindFailure) + { + ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(method); + + if (type is not RuntimeType rtType) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); + + if (method is not RuntimeMethodInfo rmi) + throw new ArgumentException(SR.Argument_MustBeRuntimeMethodInfo, nameof(method)); + + if (!rtType.IsDelegate()) + throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); + + // This API is new in Whidbey and allows the full range of delegate + // flexability (open or closed delegates binding to static or + // instance methods with relaxed signature checking. The delegate + // can also be closed over null. There's no ambiguity with all these + // options since the caller is providing us a specific MethodInfo. + Delegate? d = CreateDelegateInternal( + rtType, + rmi, + firstArgument, + DelegateBindingFlags.RelaxedSignature); + + if (d == null && throwOnBindFailure) + throw new ArgumentException(SR.Arg_DlgtTargMeth); + + return d; + } + + // + // internal implementation details (FCALLS and utilities) + // + + // V2 internal API. + internal static Delegate CreateDelegateNoSecurityCheck(Type type, object? target, RuntimeMethodHandle method) + { + ArgumentNullException.ThrowIfNull(type); + + if (method.IsNullHandle()) + throw new ArgumentNullException(nameof(method)); + + if (type is not RuntimeType rtType) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); + + if (!rtType.IsDelegate()) + throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); + + // Initialize the method... + Delegate d = InternalAlloc(rtType); + // This is a new internal API added in Whidbey. Currently it's only + // used by the dynamic method code to generate a wrapper delegate. + // Allow flexible binding options since the target method is + // unambiguously provided to us. + + if (!d.BindToMethodInfo(target, + method.GetMethodInfo(), + RuntimeMethodHandle.GetDeclaringType(method.GetMethodInfo()), + DelegateBindingFlags.RelaxedSignature)) + throw new ArgumentException(SR.Arg_DlgtTargMeth); + return d; + } + + internal static Delegate? CreateDelegateInternal(RuntimeType rtType, RuntimeMethodInfo rtMethod, object? firstArgument, DelegateBindingFlags flags) + { + Delegate d = InternalAlloc(rtType); + + if (d.BindToMethodInfo(firstArgument, rtMethod, rtMethod.GetDeclaringTypeInternal(), flags)) + return d; + else + return null; + } + + // + // internal implementation details (FCALLS and utilities) + // + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2067:ParameterDoesntMeetParameterRequirements", + Justification = "The parameter 'methodType' is passed by ref to QCallTypeHandle")] + private bool BindToMethodName(object? target, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllMethods)] RuntimeType methodType, string method, DelegateBindingFlags flags) + { + Delegate d = this; + return BindToMethodName(ObjectHandleOnStack.Create(ref d), ObjectHandleOnStack.Create(ref target), + new QCallTypeHandle(ref methodType), method, flags); + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_BindToMethodName", StringMarshalling = StringMarshalling.Utf8)] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool BindToMethodName(ObjectHandleOnStack d, ObjectHandleOnStack target, QCallTypeHandle methodType, string method, DelegateBindingFlags flags); + + private bool BindToMethodInfo(object? target, IRuntimeMethodInfo method, RuntimeType methodType, DelegateBindingFlags flags) + { + Delegate d = this; + bool ret = BindToMethodInfo(ObjectHandleOnStack.Create(ref d), ObjectHandleOnStack.Create(ref target), + method.Value, new QCallTypeHandle(ref methodType), flags); + GC.KeepAlive(method); + return ret; + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_BindToMethodInfo")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool BindToMethodInfo(ObjectHandleOnStack d, ObjectHandleOnStack target, RuntimeMethodHandleInternal method, QCallTypeHandle methodType, DelegateBindingFlags flags); + + private static MulticastDelegate InternalAlloc(RuntimeType type) + { + Debug.Assert(type.IsAssignableTo(typeof(MulticastDelegate))); + return Unsafe.As(RuntimeTypeHandle.InternalAlloc(type)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe bool InternalEqualTypes(object a, object b) + { + if (a.GetType() == b.GetType()) + return true; +#if FEATURE_TYPEEQUIVALENCE + MethodTable* pMTa = RuntimeHelpers.GetMethodTable(a); + MethodTable* pMTb = RuntimeHelpers.GetMethodTable(b); + + bool ret; + + // only use QCall to check the type equivalence scenario + if (pMTa->HasTypeEquivalence && pMTb->HasTypeEquivalence) + ret = RuntimeHelpers.AreTypesEquivalent(pMTa, pMTb); + else + ret = false; + + GC.KeepAlive(a); + GC.KeepAlive(b); + + return ret; +#else + return false; +#endif // FEATURE_TYPEEQUIVALENCE + } + + // Used by the ctor. Do not call directly. + // The name of this function will appear in managed stacktraces as delegate constructor. + private void DelegateConstruct(object target, IntPtr method) + { + // Via reflection you can pass in just about any value for the method. + // We can do some basic verification up front to prevent EE exceptions. + if (method == IntPtr.Zero) + { + throw new ArgumentNullException(nameof(method)); + } + + Delegate _this = this; + Construct(ObjectHandleOnStack.Create(ref _this), ObjectHandleOnStack.Create(ref target), method); + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_Construct")] + private static partial void Construct(ObjectHandleOnStack _this, ObjectHandleOnStack target, IntPtr method); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern unsafe void* GetMulticastInvoke(MethodTable* pMT); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_GetMulticastInvokeSlow")] + private static unsafe partial void* GetMulticastInvokeSlow(MethodTable* pMT); + + internal unsafe IntPtr GetMulticastInvoke() + { + MethodTable* pMT = RuntimeHelpers.GetMethodTable(this); + void* ptr = GetMulticastInvoke(pMT); + if (ptr == null) + { + ptr = GetMulticastInvokeSlow(pMT); + Debug.Assert(ptr != null); + Debug.Assert(ptr == GetMulticastInvoke(pMT)); + } + // No GC.KeepAlive() since the caller must keep instance alive to use returned pointer. + return (IntPtr)ptr; + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern unsafe void* GetInvokeMethod(MethodTable* pMT); + + internal unsafe IntPtr GetInvokeMethod() + { + MethodTable* pMT = RuntimeHelpers.GetMethodTable(this); + void* ptr = GetInvokeMethod(pMT); + // No GC.KeepAlive() since the caller must keep instance alive to use returned pointer. + return (IntPtr)ptr; + } + + internal static IRuntimeMethodInfo CreateMethodInfo(IntPtr methodDesc) + { + IRuntimeMethodInfo? methodInfo = null; + CreateMethodInfo(methodDesc, ObjectHandleOnStack.Create(ref methodInfo)); + return methodInfo!; + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_CreateMethodInfo")] + private static partial void CreateMethodInfo(IntPtr methodDesc, ObjectHandleOnStack retMethodInfo); + + [MethodImpl(MethodImplOptions.NoInlining)] + private IntPtr GetMethodDesc() + { + Delegate d = this; + IntPtr desc = GetMethodDesc(ObjectHandleOnStack.Create(ref d)); + _methodDesc = desc; + return desc; + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_GetMethodDesc")] + private static partial IntPtr GetMethodDesc(ObjectHandleOnStack instance); + + internal static IntPtr AdjustTarget(object target, IntPtr methodPtr) + { + return AdjustTarget(ObjectHandleOnStack.Create(ref target), methodPtr); + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_AdjustTarget")] + private static partial IntPtr AdjustTarget(ObjectHandleOnStack target, IntPtr methodPtr); + + internal void InitializeVirtualCallStub(IntPtr methodPtr) + { + Delegate d = this; + InitializeVirtualCallStub(ObjectHandleOnStack.Create(ref d), methodPtr); + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_InitializeVirtualCallStub")] + private static partial void InitializeVirtualCallStub(ObjectHandleOnStack d, IntPtr methodPtr); + + internal virtual object? GetTarget() + { + return (_methodPtrAux == IntPtr.Zero) ? _target : null; + } + + // Caches MethodInfos, added either after first request or assigned from a DynamicMethod + private static class Cache + { + public static readonly ConditionalWeakTable s_methodCache = new(); + } + } + + // These flags effect the way BindToMethodInfo and BindToMethodName are allowed to bind a delegate to a target method. Their + // values must be kept in sync with the definition in vm\comdelegate.h. + internal enum DelegateBindingFlags + { + StaticMethodOnly = 0x00000001, // Can only bind to static target methods + InstanceMethodOnly = 0x00000002, // Can only bind to instance (including virtual) methods + OpenDelegateOnly = 0x00000004, // Only allow the creation of delegates open over the 1st argument + ClosedDelegateOnly = 0x00000008, // Only allow the creation of delegates closed over the 1st argument + NeverCloseOverNull = 0x00000010, // A null target will never been considered as a possible null 1st argument + CaselessMatching = 0x00000020, // Use case insensitive lookup for methods matched by name + RelaxedSignature = 0x00000040, // Allow relaxed signature matching (co/contra variance) + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs.orig b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs.orig new file mode 100644 index 00000000000000..5319d8a8d2f6a0 --- /dev/null +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs.orig @@ -0,0 +1,592 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace System +{ + [ClassInterface(ClassInterfaceType.None)] + [ComVisible(true)] + public abstract partial class Delegate : ICloneable, ISerializable + { +<<<<<<< HEAD + // _target is the object we will invoke on + internal object? _target; // Initialized by VM as needed; null if static delegate + + // VM handle to wrapped method, null when not initialized + private IntPtr _methodDesc; +======= + // MethodBase, either cached after first request or assigned from a DynamicMethod + // For open delegates to collectible types, this may be a LoaderAllocator object + internal object? _methodBase; + + // _target is the object we will invoke on; null if static delegate + internal object? _target; // Keep _target and _methodPtr next to each other for optimal delegate invoke performance +>>>>>>> upstream/main + + // _methodPtr is a pointer to the method we will invoke + // It could be a small thunk if this is a static or UM call + internal IntPtr _methodPtr; + + // In the case of a static method passed to a delegate, this field stores + // whatever _methodPtr would have stored: and _methodPtr points to a + // small thunk which removes the "this" pointer before going on + // to _methodPtrAux. + internal IntPtr _methodPtrAux; + + internal IntPtr MethodDesc + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return _methodDesc != 0 ? _methodDesc : GetMethodDesc(); + } + } + + // This constructor is called from the class generated by the + // compiler generated code + [RequiresUnreferencedCode("The target method might be removed")] + protected Delegate(object target, string method) + { + ArgumentNullException.ThrowIfNull(target); + ArgumentNullException.ThrowIfNull(method); + + // This API existed in v1/v1.1 and only expected to create closed + // instance delegates. Constrain the call to BindToMethodName to + // such and don't allow relaxed signature matching (which could make + // the choice of target method ambiguous) for backwards + // compatibility. The name matching was case sensitive and we + // preserve that as well. + if (!BindToMethodName(target, (RuntimeType)target.GetType(), method, + DelegateBindingFlags.InstanceMethodOnly | + DelegateBindingFlags.ClosedDelegateOnly)) + throw new ArgumentException(SR.Arg_DlgtTargMeth); + } + + // This constructor is called from a class to generate a + // delegate based upon a static method name and the Type object + // for the class defining the method. + protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllMethods)] Type target, string method) + { + ArgumentNullException.ThrowIfNull(target); + ArgumentNullException.ThrowIfNull(method); + + if (target.ContainsGenericParameters) + throw new ArgumentException(SR.Arg_UnboundGenParam, nameof(target)); + if (target is not RuntimeType rtTarget) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(target)); + + // This API existed in v1/v1.1 and only expected to create open + // static delegates. Constrain the call to BindToMethodName to such + // and don't allow relaxed signature matching (which could make the + // choice of target method ambiguous) for backwards compatibility. + // The name matching was case insensitive (no idea why this is + // different from the constructor above) and we preserve that as + // well. + BindToMethodName(null, rtTarget, method, + DelegateBindingFlags.StaticMethodOnly | + DelegateBindingFlags.OpenDelegateOnly | + DelegateBindingFlags.CaselessMatching); + } + + protected virtual object? DynamicInvokeImpl(object?[]? args) + { + RuntimeMethodHandleInternal method = new RuntimeMethodHandleInternal(GetInvokeMethod()); + RuntimeMethodInfo invoke = (RuntimeMethodInfo)RuntimeType.GetMethodBase((RuntimeType)this.GetType(), method)!; + + return invoke.Invoke(this, BindingFlags.Default, null, args, null); + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj == null || !InternalEqualTypes(this, obj)) + return false; + + Delegate d = (Delegate)obj; + + // do an optimistic check first. This is hopefully cheap enough to be worth + if (_target == d._target && _methodPtr == d._methodPtr && _methodPtrAux == d._methodPtrAux) + return true; + + // even though the fields were not all equals the delegates may still match + // When target carries the delegate itself the 2 targets (delegates) may be different instances + // but the delegates are logically the same + // It may also happen that the method pointer was not jitted when creating one delegate and jitted in the other + // if that's the case the delegates may still be equals but we need to make a more complicated check + + if (_methodPtrAux == IntPtr.Zero) + { + if (d._methodPtrAux != IntPtr.Zero) + return false; // different delegate kind + + // they are both closed over the first arg + if (_target != d._target) + return false; + + // fall through method handle check + } + else + { + if (d._methodPtrAux == IntPtr.Zero) + return false; // different delegate kind + + // Ignore the target as it will be the delegate instance, though it may be a different one + /* + if (_methodPtr != d._methodPtr) + return false; + */ + + if (_methodPtrAux == d._methodPtrAux) + return true; + + // fall through method handle check + } + + return MethodDesc == d.MethodDesc; + } + + public override int GetHashCode() + { + int hashCode = MethodDesc.GetHashCode(); + if (_methodPtrAux == IntPtr.Zero && _target != null) + { + hashCode += RuntimeHelpers.GetHashCode(_target) * 33; + } + return hashCode; + } + + protected virtual MethodInfo GetMethodImpl() + { + return Cache.s_methodCache.TryGetValue(this, out MethodInfo? cachedValue) ? cachedValue : GetMethodImplUncached(); + + [MethodImpl(MethodImplOptions.NoInlining)] + MethodInfo GetMethodImplUncached() + { + Debug.Assert(this is MulticastDelegate); + MethodInfo method = Unsafe.As(this).GetMethodImplMulticast(); + + Debug.Assert(method is not null); + Cache.s_methodCache.AddOrUpdate(this, method); + return method; + } + } + + internal MethodInfo GetMethodImplSimple() + { + IRuntimeMethodInfo method = CreateMethodInfo(MethodDesc); + RuntimeType? declaringType = RuntimeMethodHandle.GetDeclaringType(method); + + // need a proper declaring type instance method on a generic type + if (declaringType.IsGenericType) + { + bool isStatic = (RuntimeMethodHandle.GetAttributes(method) & MethodAttributes.Static) != (MethodAttributes)0; + if (!isStatic) + { + if (_methodPtrAux == IntPtr.Zero) + { + // The target may be of a derived type that doesn't have visibility onto the + // target method. We don't want to call RuntimeType.GetMethodBase below with that + // or reflection can end up generating a MethodInfo where the ReflectedType cannot + // see the MethodInfo itself and that breaks an important invariant. But the + // target type could include important generic type information we need in order + // to work out what the exact instantiation of the method's declaring type is. So + // we'll walk up the inheritance chain (which will yield exactly instantiated + // types at each step) until we find the declaring type. Since the declaring type + // we get from the method is probably shared and those in the hierarchy we're + // walking won't be we compare using the generic type definition forms instead. + Type targetType = declaringType.GetGenericTypeDefinition(); + Type? currentType; + for (currentType = _target!.GetType(); currentType != null; currentType = currentType.BaseType) + { + if (currentType.IsGenericType && + currentType.GetGenericTypeDefinition() == targetType) + { + declaringType = currentType as RuntimeType; + break; + } + } + + // RCWs don't need to be "strongly-typed" in which case we don't find a base type + // that matches the declaring type of the method. This is fine because interop needs + // to work with exact methods anyway so declaringType is never shared at this point. + // The targetType may also be an interface with a Default interface method (DIM). + Debug.Assert( + currentType != null + || _target.GetType().IsCOMObject + || targetType.IsInterface, "The class hierarchy should declare the method or be a DIM"); + } + else + { + // it's an open one, need to fetch the first arg of the instantiation + MethodInfo invoke = this.GetType().GetMethod("Invoke")!; + declaringType = (RuntimeType)invoke.GetParametersAsSpan()[0].ParameterType; + } + } + } + return (MethodInfo)RuntimeType.GetMethodBase(declaringType, method)!; + } + + public object? Target => GetTarget(); + + // V1 API. + [RequiresUnreferencedCode("The target method might be removed")] + public static Delegate? CreateDelegate(Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure) + { + ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(target); + ArgumentNullException.ThrowIfNull(method); + + if (type is not RuntimeType rtType) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); + if (!rtType.IsDelegate()) + throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); + + Delegate d = InternalAlloc(rtType); + // This API existed in v1/v1.1 and only expected to create closed + // instance delegates. Constrain the call to BindToMethodName to such + // and don't allow relaxed signature matching (which could make the + // choice of target method ambiguous) for backwards compatibility. + // We never generate a closed over null delegate and this is + // actually enforced via the check on target above, but we pass + // NeverCloseOverNull anyway just for clarity. + if (!d.BindToMethodName(target, (RuntimeType)target.GetType(), method, + DelegateBindingFlags.InstanceMethodOnly | + DelegateBindingFlags.ClosedDelegateOnly | + DelegateBindingFlags.NeverCloseOverNull | + (ignoreCase ? DelegateBindingFlags.CaselessMatching : 0))) + { + if (throwOnBindFailure) + throw new ArgumentException(SR.Arg_DlgtTargMeth); + + return null; + } + + return d; + } + + // V1 API. + public static Delegate? CreateDelegate(Type type, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllMethods)] Type target, string method, bool ignoreCase, bool throwOnBindFailure) + { + ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(target); + ArgumentNullException.ThrowIfNull(method); + + if (target.ContainsGenericParameters) + throw new ArgumentException(SR.Arg_UnboundGenParam, nameof(target)); + if (type is not RuntimeType rtType) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); + if (target is not RuntimeType rtTarget) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(target)); + + if (!rtType.IsDelegate()) + throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); + + Delegate d = InternalAlloc(rtType); + // This API existed in v1/v1.1 and only expected to create open + // static delegates. Constrain the call to BindToMethodName to such + // and don't allow relaxed signature matching (which could make the + // choice of target method ambiguous) for backwards compatibility. + if (!d.BindToMethodName(null, rtTarget, method, + DelegateBindingFlags.StaticMethodOnly | + DelegateBindingFlags.OpenDelegateOnly | + (ignoreCase ? DelegateBindingFlags.CaselessMatching : 0))) + { + if (throwOnBindFailure) + throw new ArgumentException(SR.Arg_DlgtTargMeth); + + return null; + } + + return d; + } + + // V1 API. + public static Delegate? CreateDelegate(Type type, MethodInfo method, bool throwOnBindFailure) + { + ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(method); + + if (type is not RuntimeType rtType) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); + + if (method is not RuntimeMethodInfo rmi) + throw new ArgumentException(SR.Argument_MustBeRuntimeMethodInfo, nameof(method)); + + if (!rtType.IsDelegate()) + throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); + + // This API existed in v1/v1.1 and only expected to create closed + // instance delegates. Constrain the call to BindToMethodInfo to + // open delegates only for backwards compatibility. But we'll allow + // relaxed signature checking and open static delegates because + // there's no ambiguity there (the caller would have to explicitly + // pass us a static method or a method with a non-exact signature + // and the only change in behavior from v1.1 there is that we won't + // fail the call). + Delegate? d = CreateDelegateInternal( + rtType, + rmi, + null, + DelegateBindingFlags.OpenDelegateOnly | DelegateBindingFlags.RelaxedSignature); + + if (d == null && throwOnBindFailure) + throw new ArgumentException(SR.Arg_DlgtTargMeth); + + return d; + } + + // V2 API. + public static Delegate? CreateDelegate(Type type, object? firstArgument, MethodInfo method, bool throwOnBindFailure) + { + ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(method); + + if (type is not RuntimeType rtType) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); + + if (method is not RuntimeMethodInfo rmi) + throw new ArgumentException(SR.Argument_MustBeRuntimeMethodInfo, nameof(method)); + + if (!rtType.IsDelegate()) + throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); + + // This API is new in Whidbey and allows the full range of delegate + // flexability (open or closed delegates binding to static or + // instance methods with relaxed signature checking. The delegate + // can also be closed over null. There's no ambiguity with all these + // options since the caller is providing us a specific MethodInfo. + Delegate? d = CreateDelegateInternal( + rtType, + rmi, + firstArgument, + DelegateBindingFlags.RelaxedSignature); + + if (d == null && throwOnBindFailure) + throw new ArgumentException(SR.Arg_DlgtTargMeth); + + return d; + } + + // + // internal implementation details (FCALLS and utilities) + // + + // V2 internal API. + internal static Delegate CreateDelegateNoSecurityCheck(Type type, object? target, RuntimeMethodHandle method) + { + ArgumentNullException.ThrowIfNull(type); + + if (method.IsNullHandle()) + throw new ArgumentNullException(nameof(method)); + + if (type is not RuntimeType rtType) + throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); + + if (!rtType.IsDelegate()) + throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); + + // Initialize the method... + Delegate d = InternalAlloc(rtType); + // This is a new internal API added in Whidbey. Currently it's only + // used by the dynamic method code to generate a wrapper delegate. + // Allow flexible binding options since the target method is + // unambiguously provided to us. + + if (!d.BindToMethodInfo(target, + method.GetMethodInfo(), + RuntimeMethodHandle.GetDeclaringType(method.GetMethodInfo()), + DelegateBindingFlags.RelaxedSignature)) + throw new ArgumentException(SR.Arg_DlgtTargMeth); + return d; + } + + internal static Delegate? CreateDelegateInternal(RuntimeType rtType, RuntimeMethodInfo rtMethod, object? firstArgument, DelegateBindingFlags flags) + { + Delegate d = InternalAlloc(rtType); + + if (d.BindToMethodInfo(firstArgument, rtMethod, rtMethod.GetDeclaringTypeInternal(), flags)) + return d; + else + return null; + } + + // + // internal implementation details (FCALLS and utilities) + // + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2067:ParameterDoesntMeetParameterRequirements", + Justification = "The parameter 'methodType' is passed by ref to QCallTypeHandle")] + private bool BindToMethodName(object? target, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllMethods)] RuntimeType methodType, string method, DelegateBindingFlags flags) + { + Delegate d = this; + return BindToMethodName(ObjectHandleOnStack.Create(ref d), ObjectHandleOnStack.Create(ref target), + new QCallTypeHandle(ref methodType), method, flags); + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_BindToMethodName", StringMarshalling = StringMarshalling.Utf8)] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool BindToMethodName(ObjectHandleOnStack d, ObjectHandleOnStack target, QCallTypeHandle methodType, string method, DelegateBindingFlags flags); + + private bool BindToMethodInfo(object? target, IRuntimeMethodInfo method, RuntimeType methodType, DelegateBindingFlags flags) + { + Delegate d = this; + bool ret = BindToMethodInfo(ObjectHandleOnStack.Create(ref d), ObjectHandleOnStack.Create(ref target), + method.Value, new QCallTypeHandle(ref methodType), flags); + GC.KeepAlive(method); + return ret; + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_BindToMethodInfo")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool BindToMethodInfo(ObjectHandleOnStack d, ObjectHandleOnStack target, RuntimeMethodHandleInternal method, QCallTypeHandle methodType, DelegateBindingFlags flags); + + private static MulticastDelegate InternalAlloc(RuntimeType type) + { + Debug.Assert(type.IsAssignableTo(typeof(MulticastDelegate))); + return Unsafe.As(RuntimeTypeHandle.InternalAlloc(type)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe bool InternalEqualTypes(object a, object b) + { + if (a.GetType() == b.GetType()) + return true; +#if FEATURE_TYPEEQUIVALENCE + MethodTable* pMTa = RuntimeHelpers.GetMethodTable(a); + MethodTable* pMTb = RuntimeHelpers.GetMethodTable(b); + + bool ret; + + // only use QCall to check the type equivalence scenario + if (pMTa->HasTypeEquivalence && pMTb->HasTypeEquivalence) + ret = RuntimeHelpers.AreTypesEquivalent(pMTa, pMTb); + else + ret = false; + + GC.KeepAlive(a); + GC.KeepAlive(b); + + return ret; +#else + return false; +#endif // FEATURE_TYPEEQUIVALENCE + } + + // Used by the ctor. Do not call directly. + // The name of this function will appear in managed stacktraces as delegate constructor. + private void DelegateConstruct(object target, IntPtr method) + { + // Via reflection you can pass in just about any value for the method. + // We can do some basic verification up front to prevent EE exceptions. + if (method == IntPtr.Zero) + { + throw new ArgumentNullException(nameof(method)); + } + + Delegate _this = this; + Construct(ObjectHandleOnStack.Create(ref _this), ObjectHandleOnStack.Create(ref target), method); + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_Construct")] + private static partial void Construct(ObjectHandleOnStack _this, ObjectHandleOnStack target, IntPtr method); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern unsafe void* GetMulticastInvoke(MethodTable* pMT); + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_GetMulticastInvokeSlow")] + private static unsafe partial void* GetMulticastInvokeSlow(MethodTable* pMT); + + internal unsafe IntPtr GetMulticastInvoke() + { + MethodTable* pMT = RuntimeHelpers.GetMethodTable(this); + void* ptr = GetMulticastInvoke(pMT); + if (ptr == null) + { + ptr = GetMulticastInvokeSlow(pMT); + Debug.Assert(ptr != null); + Debug.Assert(ptr == GetMulticastInvoke(pMT)); + } + // No GC.KeepAlive() since the caller must keep instance alive to use returned pointer. + return (IntPtr)ptr; + } + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern unsafe void* GetInvokeMethod(MethodTable* pMT); + + internal unsafe IntPtr GetInvokeMethod() + { + MethodTable* pMT = RuntimeHelpers.GetMethodTable(this); + void* ptr = GetInvokeMethod(pMT); + // No GC.KeepAlive() since the caller must keep instance alive to use returned pointer. + return (IntPtr)ptr; + } + + internal static IRuntimeMethodInfo CreateMethodInfo(IntPtr methodDesc) + { + IRuntimeMethodInfo? methodInfo = null; + CreateMethodInfo(methodDesc, ObjectHandleOnStack.Create(ref methodInfo)); + return methodInfo!; + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_CreateMethodInfo")] + private static partial void CreateMethodInfo(IntPtr methodDesc, ObjectHandleOnStack retMethodInfo); + + [MethodImpl(MethodImplOptions.NoInlining)] + private IntPtr GetMethodDesc() + { + Delegate d = this; + IntPtr desc = GetMethodDesc(ObjectHandleOnStack.Create(ref d)); + _methodDesc = desc; + return desc; + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_GetMethodDesc")] + private static partial IntPtr GetMethodDesc(ObjectHandleOnStack instance); + + internal static IntPtr AdjustTarget(object target, IntPtr methodPtr) + { + return AdjustTarget(ObjectHandleOnStack.Create(ref target), methodPtr); + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_AdjustTarget")] + private static partial IntPtr AdjustTarget(ObjectHandleOnStack target, IntPtr methodPtr); + + internal void InitializeVirtualCallStub(IntPtr methodPtr) + { + Delegate d = this; + InitializeVirtualCallStub(ObjectHandleOnStack.Create(ref d), methodPtr); + } + + [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_InitializeVirtualCallStub")] + private static partial void InitializeVirtualCallStub(ObjectHandleOnStack d, IntPtr methodPtr); + + internal virtual object? GetTarget() + { + return (_methodPtrAux == IntPtr.Zero) ? _target : null; + } + + // Caches MethodInfos, added either after first request or assigned from a DynamicMethod + private static class Cache + { + public static readonly ConditionalWeakTable s_methodCache = new(); + } + } + + // These flags effect the way BindToMethodInfo and BindToMethodName are allowed to bind a delegate to a target method. Their + // values must be kept in sync with the definition in vm\comdelegate.h. + internal enum DelegateBindingFlags + { + StaticMethodOnly = 0x00000001, // Can only bind to static target methods + InstanceMethodOnly = 0x00000002, // Can only bind to instance (including virtual) methods + OpenDelegateOnly = 0x00000004, // Only allow the creation of delegates open over the 1st argument + ClosedDelegateOnly = 0x00000008, // Only allow the creation of delegates closed over the 1st argument + NeverCloseOverNull = 0x00000010, // A null target will never been considered as a possible null 1st argument + CaselessMatching = 0x00000020, // Use case insensitive lookup for methods matched by name + RelaxedSignature = 0x00000040, // Allow relaxed signature matching (co/contra variance) + } +} diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index 81347180699bd5..417b2a9792ea04 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -17,24 +17,6 @@ namespace System [ComVisible(true)] public abstract partial class MulticastDelegate : Delegate { - // This is set under 4 circumstances - // 1. Multicast delegate - // 2. Wrapper delegate - // 3. DynamicMethods - // 4. Collectible delegates - private object? _invocationList; // Initialized by VM as needed - private nint _invocationCount; - - internal bool IsUnmanagedFunctionPtr() - { - return _invocationCount == -1; - } - - internal bool InvocationListLogicallyNull() - { - return _invocationList is null or LoaderAllocator or DynamicResolver or DynamicMethod; - } - [Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] [EditorBrowsable(EditorBrowsableState.Never)] public override void GetObjectData(SerializationInfo info, StreamingContext context) @@ -57,135 +39,86 @@ public sealed override bool Equals([NotNullWhen(true)] object? obj) // the types are the same, obj should also be a // MulticastDelegate Debug.Assert(obj is MulticastDelegate, "Shouldn't have failed here since we already checked the types are the same!"); - MulticastDelegate d = Unsafe.As(obj); + MulticastDelegate other = Unsafe.AsAssert(obj); - if (_invocationCount != 0) + if (IsSpecialDelegate) { // there are 4 kind of delegate kinds that fall into this bucket - // 1- Multicast (_invocationList is Object[]) - // 2- Wrapper (_invocationList is Delegate) - // 3- Unmanaged FntPtr (_invocationList == null) - // 4- Open virtual (_invocationCount == MethodDesc of target, _invocationList == null, LoaderAllocator, or DynamicResolver) - - if (InvocationListLogicallyNull()) + // 1- Multicast (_helperObject is Object[]) + // 2- Wrapper (_helperObject is Delegate) + // 3- Unmanaged FntPtr (_helperObject == null) + // 4- Open virtual (_invocationCount == MethodDesc of target, _helperObject == null, LoaderAllocator, or DynamicResolver) + switch (_helperObject) { - if (IsUnmanagedFunctionPtr()) - { - if (!d.IsUnmanagedFunctionPtr()) - return false; - - return _methodPtr == d._methodPtr - && _methodPtrAux == d._methodPtrAux; - } + case object[]: + return GetInvocationsUnchecked().SequenceEqual(other.GetInvocationsUnchecked()); + // this is a wrapper delegate so we need to unwrap and check the inner one + case Delegate wrapper: + return wrapper.Equals(other); + default: + if (IsUnmanagedFunctionPtr) + { + return other.IsUnmanagedFunctionPtr && + _functionPointer == other._functionPointer && + _extraFunctionPointerOrData == other._extraFunctionPointerOrData; + } - // now we know 'this' is not a special one, so we can work out what the other is - if (d._invocationList is Delegate) + // now we know 'this' is not a special one, so we can work out what the other is // this is a wrapper delegate so we need to unwrap and check the inner one - return Equals(d._invocationList); - - return base.Equals(obj); - } - else - { - if (_invocationList is Delegate invocationListDelegate) - { - // this is a wrapper delegate so we need to unwrap and check the inner one - return invocationListDelegate.Equals(obj); - } - else - { - Debug.Assert(_invocationList is object[], "empty invocation list on multicast delegate"); - return InvocationListEquals(d); - } + return other._helperObject is Delegate ? Equals(other._helperObject) : base.Equals(obj); } } - else - { - // among the several kind of delegates falling into this bucket one has got a non - // empty _invocationList (open static with special sig) - // to be equals we need to check that _invocationList matches (both null is fine) - // and call the base.Equals() - if (!InvocationListLogicallyNull()) - { - if (!_invocationList!.Equals(d._invocationList)) - return false; - return base.Equals(d); - } - - // now we know 'this' is not a special one, so we can work out what the other is - if (d._invocationList is Delegate) - // this is a wrapper delegate so we need to unwrap and check the inner one - return Equals(d._invocationList); - // now we can call on the base - return base.Equals(d); - } - } - - // Recursive function which will check for equality of the invocation list. - private bool InvocationListEquals(MulticastDelegate d) - { - Debug.Assert(d != null); - Debug.Assert(_invocationList is object[]); - object[] invocationList = (object[])_invocationList; - - if (d._invocationCount != _invocationCount) - return false; - - int invocationCount = (int)_invocationCount; - for (int i = 0; i < invocationCount; i++) - { - Debug.Assert(invocationList[i] is Delegate); - Delegate dd = (Delegate)invocationList[i]; // If invocationList is an object[], it always contains Delegate (or MulticastDelegate) objects - - object[] dInvocationList = (d._invocationList as object[])!; - if (!dd.Equals(dInvocationList[i])) - return false; - } - return true; + Debug.Assert(InvocationListLogicallyNull); + // now we know 'this' is not a special one, so we can work out what the other is + return other._helperObject is Delegate ? + // this is a wrapper delegate so we need to unwrap and check the inner one + Equals(other._helperObject) : + // we can call on the base + base.Equals(other); } private static bool TrySetSlot(object?[] a, int index, object o) { if (a[index] == null && Interlocked.CompareExchange(ref a[index], o, null) == null) + { return true; + } // The slot may be already set because we have added and removed the same method before. // Optimize this case, because it's cheaper than copying the array. - if (a[index] is object ai) + object? previous = a[index]; + if (previous is null) { - MulticastDelegate d = (MulticastDelegate)o; - MulticastDelegate dd = (MulticastDelegate)ai; - - if (dd._methodPtr == d._methodPtr && - dd._target == d._target && - dd._methodPtrAux == d._methodPtrAux) - { - return true; - } + return false; } - return false; + + MulticastDelegate d = (MulticastDelegate)o; + MulticastDelegate dd = (MulticastDelegate)previous; + return dd._functionPointer == d._functionPointer && + dd._extraFunctionPointerOrData == d._extraFunctionPointerOrData && + dd._firstParameter == d._firstParameter; } private unsafe MulticastDelegate NewMulticastDelegate(object[] invocationList, int invocationCount, bool thisIsMultiCastAlready) { // First, allocate a new multicast delegate just like this one, i.e. same type as the this object - MulticastDelegate result = Unsafe.As(RuntimeTypeHandle.InternalAllocNoChecks(RuntimeHelpers.GetMethodTable(this))); + MulticastDelegate result = Unsafe.AsAssert(RuntimeTypeHandle.InternalAllocNoChecks(RuntimeHelpers.GetMethodTable(this))); // Performance optimization - if this already points to a true multicast delegate, - // copy _methodPtr and _methodPtrAux fields rather than calling into the EE to get them + // copy _functionPointer and _functionPointerAux fields rather than calling into the EE to get them if (thisIsMultiCastAlready) { - result._methodPtr = this._methodPtr; - result._methodPtrAux = this._methodPtrAux; + result._functionPointer = _functionPointer; + result._extraFunctionPointerOrData = _extraFunctionPointerOrData; } else { - result._methodPtr = GetMulticastInvoke(); - result._methodPtrAux = GetInvokeMethod(); + result._functionPointer = GetMulticastInvoke(); + result._extraFunctionPointerOrData = GetInvokeMethod(); } - result._target = result; - result._invocationList = invocationList; + result._firstParameter = result; + result._helperObject = invocationList; result._invocationCount = invocationCount; return result; @@ -196,22 +129,6 @@ internal MulticastDelegate NewMulticastDelegate(object[] invocationList, int inv return NewMulticastDelegate(invocationList, invocationCount, false); } - internal void StoreDynamicMethod(MethodInfo dynamicMethod) - { - if (_invocationCount != 0) - { - Debug.Assert(!IsUnmanagedFunctionPtr(), "dynamic method and unmanaged fntptr delegate combined"); - // must be a secure/wrapper one, unwrap and save - MulticastDelegate d = ((MulticastDelegate?)_invocationList)!; - d.StoreDynamicMethod(dynamicMethod); - } - else - { - Debug.Assert(InvocationListLogicallyNull(), "dynamic method with invocation list"); - _invocationList = dynamicMethod; - } - } - // This method will combine this delegate with the passed delegate // to form a new delegate. protected sealed override Delegate CombineImpl(Delegate? follow) @@ -226,12 +143,12 @@ protected sealed override Delegate CombineImpl(Delegate? follow) MulticastDelegate dFollow = (MulticastDelegate)follow; object[]? resultList; int followCount = 1; - object[]? followList = dFollow._invocationList as object[]; + object[]? followList = dFollow._helperObject as object[]; if (followList != null) followCount = (int)dFollow._invocationCount; int resultCount; - if (_invocationList is not object[] invocationList) + if (_helperObject is not object[] invocationList) { resultCount = 1 + followCount; resultList = new object[resultCount]; @@ -247,61 +164,62 @@ protected sealed override Delegate CombineImpl(Delegate? follow) } return NewMulticastDelegate(resultList, resultCount); } - else + + int invocationCount = (int)_invocationCount; + resultCount = invocationCount + followCount; + resultList = null; + if (resultCount <= invocationList.Length) { - int invocationCount = (int)_invocationCount; - resultCount = invocationCount + followCount; - resultList = null; - if (resultCount <= invocationList.Length) + resultList = invocationList; + if (followList == null) { - resultList = invocationList; - if (followList == null) - { - if (!TrySetSlot(resultList, invocationCount, dFollow)) - resultList = null; - } - else + if (!TrySetSlot(resultList, invocationCount, dFollow)) + resultList = null; + } + else + { + for (int i = 0; i < followCount; i++) { - for (int i = 0; i < followCount; i++) + if (TrySetSlot(resultList, invocationCount + i, followList[i])) { - if (!TrySetSlot(resultList, invocationCount + i, followList[i])) - { - resultList = null; - break; - } + continue; } + + resultList = null; + break; } } + } - if (resultList == null) - { - int allocCount = invocationList.Length; - while (allocCount < resultCount) - allocCount *= 2; + if (resultList == null) + { + int allocCount = invocationList.Length; + while (allocCount < resultCount) + allocCount *= 2; - resultList = new object[allocCount]; + resultList = new object[allocCount]; - for (int i = 0; i < invocationCount; i++) - resultList[i] = invocationList[i]; + for (int i = 0; i < invocationCount; i++) + resultList[i] = invocationList[i]; - if (followList == null) - { - resultList[invocationCount] = dFollow; - } - else - { - for (int i = 0; i < followCount; i++) - resultList[invocationCount + i] = followList[i]; - } + if (followList == null) + { + resultList[invocationCount] = dFollow; + } + else + { + for (int i = 0; i < followCount; i++) + resultList[invocationCount + i] = followList[i]; } - return NewMulticastDelegate(resultList, resultCount, true); } + return NewMulticastDelegate(resultList, resultCount, true); } private object[] DeleteFromInvocationList(object[] invocationList, int invocationCount, int deleteIndex, int deleteCount) { - Debug.Assert(_invocationList is object[]); - object[] thisInvocationList = (object[])_invocationList; + object[]? thisInvocationList = Unsafe.AsAssert(_helperObject); + Debug.Assert(thisInvocationList is not null); + int allocCount = thisInvocationList.Length; while (allocCount / 2 >= invocationCount - deleteCount) allocCount /= 2; @@ -332,72 +250,74 @@ private static bool EqualInvocationLists(object[] a, object[] b, int start, int // look at the invocation list.) If this is found we remove it from // this list and return a new delegate. If its not found a copy of the // current list is returned. - protected sealed override Delegate? RemoveImpl(Delegate value) + protected sealed override Delegate? RemoveImpl(Delegate? value) { // There is a special case were we are removing using a delegate as // the value we need to check for this case // - MulticastDelegate? v = value as MulticastDelegate; - + MulticastDelegate? v = (MulticastDelegate?)value; if (v == null) return this; - if (v._invocationList is not object[]) + + if (!v.HasSingleTarget) { - if (_invocationList is not object[] invocationList) - { - // they are both not real Multicast - if (this.Equals(value)) - return null; - } - else + if (_helperObject is object[] invocationList) { int invocationCount = (int)_invocationCount; - for (int i = invocationCount; --i >= 0;) + int vInvocationCount = (int)v._invocationCount; + object[] vInvocationList = (object[])v._helperObject!; + for (int i = invocationCount - vInvocationCount; i >= 0; i--) { - if (value.Equals(invocationList[i])) + if (!EqualInvocationLists(invocationList, vInvocationList, i, vInvocationCount)) + { + continue; + } + + if (invocationCount - vInvocationCount == 0) + { + // Special case - no values left + return null; + } + + if (invocationCount - vInvocationCount == 1) { - if (invocationCount == 2) - { - // Special case - only one value left, either at the beginning or the end - return (Delegate)invocationList[1 - i]; - } - else - { - object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, 1); - return NewMulticastDelegate(list, invocationCount - 1, true); - } + // Special case - only one value left, either at the beginning or the end + return (Delegate)invocationList[i != 0 ? 0 : invocationCount - 1]; } + + object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, vInvocationCount); + return NewMulticastDelegate(list, invocationCount - vInvocationCount, true); } } } else { - if (_invocationList is object[] invocationList) + if (_helperObject is object[] invocationList) { int invocationCount = (int)_invocationCount; - int vInvocationCount = (int)v._invocationCount; - for (int i = invocationCount - vInvocationCount; i >= 0; i--) + for (int i = invocationCount; --i >= 0;) { - if (EqualInvocationLists(invocationList, (v._invocationList as object[])!, i, vInvocationCount)) + if (!value!.Equals(invocationList[i])) + { + continue; + } + + if (invocationCount == 2) { - if (invocationCount - vInvocationCount == 0) - { - // Special case - no values left - return null; - } - else if (invocationCount - vInvocationCount == 1) - { - // Special case - only one value left, either at the beginning or the end - return (Delegate)invocationList[i != 0 ? 0 : invocationCount - 1]; - } - else - { - object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, vInvocationCount); - return NewMulticastDelegate(list, invocationCount - vInvocationCount, true); - } + // Special case - only one value left, either at the beginning or the end + return (Delegate)invocationList[1 - i]; } + + object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, 1); + return NewMulticastDelegate(list, invocationCount - 1, true); } } + else + { + // they are both not real Multicast + if (Equals(value)) + return null; + } } return this; @@ -406,141 +326,91 @@ private static bool EqualInvocationLists(object[] a, object[] b, int start, int // This method returns the Invocation list of this multicast delegate. public sealed override Delegate[] GetInvocationList() { - Delegate[] del; - if (_invocationList is not object[] invocationList) + if (_helperObject is not object[]) { - del = new Delegate[1]; - del[0] = this; + return [this]; } - else - { - // Create an array of delegate copies and each - // element into the array - del = new Delegate[(int)_invocationCount]; - for (int i = 0; i < del.Length; i++) - del[i] = (Delegate)invocationList[i]; - } + // Create an array of delegate copies and copy each element into the array + ReadOnlySpan invocationList = GetInvocationsUnchecked(); + Delegate[] del = new Delegate[invocationList.Length]; + for (int i = 0; i < invocationList.Length; i++) + del[i] = invocationList[i]; return del; } - internal new bool HasSingleTarget => _invocationList is not object[]; + internal new bool HasSingleTarget => _helperObject is not object[]; // Used by delegate invocation list enumerator - internal object? /* Delegate? */ TryGetAt(int index) + internal MulticastDelegate? TryGetAt(int index) { - if (_invocationList is not object[] invocationList) + if (HasSingleTarget) { - return (index == 0) ? this : null; + if (index == 0) + return this; } else { - return ((uint)index < (uint)_invocationCount) ? invocationList[index] : null; + ReadOnlySpan invocationList = GetInvocationsUnchecked(); + if ((uint)index < (uint)invocationList.Length) + return invocationList[index]; } + + return null; } public sealed override int GetHashCode() { - if (IsUnmanagedFunctionPtr()) - return HashCode.Combine(_methodPtr, _methodPtrAux); + if (IsUnmanagedFunctionPtr) + return HashCode.Combine(_functionPointer, _functionPointer); - if (_invocationCount != 0) + if (IsSpecialDelegate) { - if (_invocationList is Delegate t) + switch (_helperObject) { - // this is a wrapper delegate so we need to unwrap and check the inner one - return t.GetHashCode(); - } - } + case object[]: + { + int hash = 0; + foreach (MulticastDelegate multicastDelegate in GetInvocationsUnchecked()) + { + hash = hash * 33 + multicastDelegate.GetHashCode(); + } - if (_invocationList is not object[] invocationList) - { - return base.GetHashCode(); - } - else - { - int hash = 0; - for (int i = 0; i < (int)_invocationCount; i++) - { - hash = hash * 33 + invocationList[i].GetHashCode(); + return hash; + } + case Delegate wrapper: + // this is a wrapper delegate so we need to unwrap and check the inner one + return wrapper.GetHashCode(); } - - return hash; } + + return base.GetHashCode(); } internal override object? GetTarget() { - if (_invocationCount != 0) + if (IsSpecialDelegate) { - // _invocationCount != 0 we are in one of these cases: + // IsSpecialDelegate we are in one of these cases: // - Multicast -> return the target of the last delegate in the list // - Wrapper delegate -> return the target of the inner delegate // - unmanaged function pointer - return null // - virtual open delegate - return null - if (InvocationListLogicallyNull()) + if (InvocationListLogicallyNull) { // both open virtual and ftn pointer return null for the target return null; } - else - { - if (_invocationList is object[] invocationList) - { - int invocationCount = (int)_invocationCount; - return ((Delegate)invocationList[invocationCount - 1]).GetTarget(); - } - else - { - if (_invocationList is Delegate receiver) - return receiver.GetTarget(); - } - } - } - return base.GetTarget(); - } - - internal MethodInfo GetMethodImplMulticast() - { - if (_invocationCount != 0 && _invocationList != null) - { - // multicast case - if (_invocationList is object[] invocationList) - { - int index = (int)_invocationCount - 1; - return ((Delegate)invocationList[index]).Method; - } - if (_invocationList is MulticastDelegate innerDelegate) + switch (_helperObject) { - // must be a wrapper delegate - return innerDelegate.GetMethodImpl(); + case object[]: + return GetLastInvocationUnchecked().Target; + case Delegate receiver: + return receiver.Target; } } - else if (_invocationList is DynamicMethod dynamicMethod) - { - return dynamicMethod; - } - else if (IsUnmanagedFunctionPtr()) - { - // we handle unmanaged function pointers here because the generic ones (used for WinRT) would otherwise - // be treated as open delegates by the base implementation, resulting in failure to get the MethodInfo - IRuntimeMethodInfo method = CreateMethodInfo(MethodDesc); - RuntimeType declaringType = RuntimeMethodHandle.GetDeclaringType(method); - - // need a proper declaring type instance method on a generic type - if (declaringType.IsGenericType) - { - // we are returning the 'Invoke' method of this delegate so use this.GetType() for the exact type - RuntimeType reflectedType = (RuntimeType)GetType(); - declaringType = reflectedType; - } - - return (MethodInfo)RuntimeType.GetMethodBase(declaringType, method)!; - } - - // Otherwise, must be a non multicast delegate. - return GetMethodImplSimple(); + return base.GetTarget(); } // this should help inlining @@ -556,16 +426,16 @@ private void CtorClosed(object target, IntPtr methodPtr) { if (target == null) ThrowNullThisInDelegateToInstance(); - this._target = target; - this._methodPtr = methodPtr; + _firstParameter = target; + _functionPointer = (nuint)methodPtr; } [DebuggerNonUserCode] [DebuggerStepThrough] private void CtorClosedStatic(object target, IntPtr methodPtr) { - this._target = target; - this._methodPtr = methodPtr; + _firstParameter = target; + _functionPointer = (nuint)methodPtr; } [DebuggerNonUserCode] @@ -574,58 +444,58 @@ private void CtorRTClosed(object target, IntPtr methodPtr) { if (target == null) ThrowNullThisInDelegateToInstance(); - this._target = target; - this._methodPtr = AdjustTarget(target, methodPtr); + _firstParameter = target; + _functionPointer = (nuint)AdjustTarget(target, methodPtr); } [DebuggerNonUserCode] [DebuggerStepThrough] private void CtorOpened(object target, IntPtr methodPtr, IntPtr shuffleThunk) { - this._target = this; - this._methodPtr = shuffleThunk; - this._methodPtrAux = methodPtr; + _firstParameter = this; + _functionPointer = (nuint)shuffleThunk; + _extraFunctionPointerOrData = methodPtr; } [DebuggerNonUserCode] [DebuggerStepThrough] private void CtorVirtualDispatch(object target, IntPtr methodPtr, IntPtr shuffleThunk) { - this._target = this; - this._methodPtr = shuffleThunk; - this.InitializeVirtualCallStub(methodPtr); + _firstParameter = this; + _functionPointer = (nuint)shuffleThunk; + InitializeVirtualCallStub(methodPtr); } [DebuggerNonUserCode] [DebuggerStepThrough] private void CtorCollectibleClosedStatic(object target, IntPtr methodPtr, IntPtr gchandle) { - this._target = target; - this._methodPtr = methodPtr; - this._invocationList = GCHandle.InternalGet(gchandle); - Debug.Assert(InvocationListLogicallyNull()); + _firstParameter = target; + _functionPointer = (nuint)methodPtr; + _helperObject = GCHandle.InternalGet(gchandle); + Debug.Assert(InvocationListLogicallyNull); } [DebuggerNonUserCode] [DebuggerStepThrough] private void CtorCollectibleOpened(object target, IntPtr methodPtr, IntPtr shuffleThunk, IntPtr gchandle) { - this._target = this; - this._methodPtr = shuffleThunk; - this._methodPtrAux = methodPtr; - this._invocationList = GCHandle.InternalGet(gchandle); - Debug.Assert(InvocationListLogicallyNull()); + _firstParameter = this; + _functionPointer = (nuint)shuffleThunk; + _extraFunctionPointerOrData = methodPtr; + _helperObject = GCHandle.InternalGet(gchandle); + Debug.Assert(InvocationListLogicallyNull); } [DebuggerNonUserCode] [DebuggerStepThrough] private void CtorCollectibleVirtualDispatch(object target, IntPtr methodPtr, IntPtr shuffleThunk, IntPtr gchandle) { - this._target = this; - this._methodPtr = shuffleThunk; - this._invocationList = GCHandle.InternalGet(gchandle); - Debug.Assert(InvocationListLogicallyNull()); - this.InitializeVirtualCallStub(methodPtr); + _firstParameter = this; + _functionPointer = (nuint)shuffleThunk; + _helperObject = GCHandle.InternalGet(gchandle); + Debug.Assert(InvocationListLogicallyNull); + InitializeVirtualCallStub(methodPtr); } #pragma warning restore IDE0060 } diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index d282c4e45ddd84..3e39ae3814e715 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -3269,7 +3269,7 @@ DacDbiInterfaceImpl::DelegateType DacDbiInterfaceImpl::GetDelegateType(VMPTR_Obj // If this delegate points to a static function or this is a open virtual delegate, this should be non-null // Special case: This might fail in a VSD delegate (instance open virtual)... // TODO: There is the special signatures cases missing. - TADDR targetMethodPtr = PCODEToPINSTR(pDelObj->GetMethodPtrAux()); + TADDR targetMethodPtr = PCODEToPINSTR(pDelObj->GetExtraFunctionPointerOrData()); if (targetMethodPtr == (TADDR)NULL) { // Static extension methods, other closed static delegates, and instance delegates fall into this category. @@ -3306,10 +3306,10 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetDelegateFunctionData( switch (GetDelegateType(delegateObject)) { case kClosedDelegate: - targetMethodPtr = PCODEToPINSTR(pDelObj->GetMethodPtr()); + targetMethodPtr = PCODEToPINSTR(pDelObj->GetFunctionPointer()); break; case kOpenDelegate: - targetMethodPtr = PCODEToPINSTR(pDelObj->GetMethodPtrAux()); + targetMethodPtr = PCODEToPINSTR(pDelObj->GetExtraFunctionPointerOrData()); break; default: ThrowHR(CORDBG_E_UNSUPPORTED_DELEGATE); @@ -3345,7 +3345,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetDelegateTargetObject( { case kClosedDelegate: { - PTR_Object pRemoteTargetObj = OBJECTREFToObject(pDelObj->GetTarget()); + PTR_Object pRemoteTargetObj = OBJECTREFToObject(pDelObj->GetFirstParameter()); ppTargetObj->SetDacTargetPtr(pRemoteTargetObj.GetAddr()); break; } diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index 52a3fbe68d7024..57eecbb07ed79d 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -8262,7 +8262,7 @@ void DebuggerStepper::TriggerMulticastDelegate(DELEGATEREF pDel, INT32 delegateC TraceDestination trace; FramePointer fp = LEAF_MOST_FRAME; - PTRARRAYREF pDelInvocationList = (PTRARRAYREF) pDel->GetInvocationList(); + PTRARRAYREF pDelInvocationList = (PTRARRAYREF) pDel->GetHelperObject(); DELEGATEREF pCurrentInvokeDel = (DELEGATEREF) pDelInvocationList->GetAt(delegateCount); StubLinkStubManager::TraceDelegateObject((BYTE*)OBJECTREFToObject(pCurrentInvokeDel), &trace); diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 3f09ac4fba8406..c8b6019f5eb326 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -1183,15 +1183,15 @@ GenTree* Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call, #endif if (targetMethodHnd != nullptr) { - CORINFO_METHOD_HANDLE alternateCtor = nullptr; DelegateCtorArgs ctorData; ctorData.pMethod = info.compMethodHnd; ctorData.pArg3 = nullptr; ctorData.pArg4 = nullptr; ctorData.pArg5 = nullptr; - alternateCtor = info.compCompHnd->GetDelegateCtor(methHnd, clsHnd, targetMethodHnd, &ctorData); - if (alternateCtor != methHnd) + CORINFO_METHOD_HANDLE alternateCtor = + info.compCompHnd->GetDelegateCtor(methHnd, clsHnd, targetMethodHnd, &ctorData); + if (alternateCtor != NO_METHOD_HANDLE && alternateCtor != methHnd) { JITDUMP("optimized\n"); // we erase any inline info that may have been set for generics has it is not needed here, diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index e11a597e10ce28..0ae50a3cae4277 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3441,9 +3441,9 @@ private void getEEInfo(ref CORINFO_EE_INFO pEEInfoOut) pEEInfoOut.inlinedCallFrameInfo.size = (uint)SizeOfPInvokeTransitionFrame; - // _target/_firstParameter + // _firstParameter pEEInfoOut.offsetOfDelegateInstance = 2 * (uint)pointerSize; - // _methodPtr/_functionPointer + // _functionPointer pEEInfoOut.offsetOfDelegateFirstTarget = pEEInfoOut.offsetOfDelegateInstance + (uint)pointerSize; pEEInfoOut.sizeOfReversePInvokeFrame = (uint)SizeOfReversePInvokeTransitionFrame; diff --git a/src/coreclr/vm/amd64/asmconstants.h b/src/coreclr/vm/amd64/asmconstants.h index f6507886d6bda0..26a29a1974fac6 100644 --- a/src/coreclr/vm/amd64/asmconstants.h +++ b/src/coreclr/vm/amd64/asmconstants.h @@ -443,9 +443,9 @@ ASMCONSTANTS_C_ASSERT(OFFSET__TEB__ThreadLocalStoragePointer == offsetof(TEB, Th #define Thread__ObjectRefFlush ?ObjectRefFlush@Thread@@SAXPEAV1@@Z -#define DELEGATE_FIELD_OFFSET__METHOD_AUX 0x20 -ASMCONSTANTS_RUNTIME_ASSERT(DELEGATE_FIELD_OFFSET__METHOD_AUX == Object::GetOffsetOfFirstField() + - CoreLibBinder::GetFieldOffset(FIELD__DELEGATE__METHOD_PTR_AUX)); +#define DELEGATE_FIELD_OFFSET__EXTRA_FUNCTION_POINTER_OR_DATA 0x20 +ASMCONSTANTS_RUNTIME_ASSERT(DELEGATE_FIELD_OFFSET__EXTRA_FUNCTION_POINTER_OR_DATA == Object::GetOffsetOfFirstField() + + CoreLibBinder::GetFieldOffset(FIELD__DELEGATE__EXTRA_FUNCTION_POINTER_OR_DATA)); #define MAX_STRING_LENGTH 0x3FFFFFDF diff --git a/src/coreclr/vm/arm/stubs.cpp b/src/coreclr/vm/arm/stubs.cpp index 815ab68867a005..298b8823e5497b 100644 --- a/src/coreclr/vm/arm/stubs.cpp +++ b/src/coreclr/vm/arm/stubs.cpp @@ -939,7 +939,7 @@ VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) // On entry r0 holds the delegate instance. Look up the real target address stored in the MethodPtrAux // field and stash it in r12. // ldr r12, [r0, #offsetof(DelegateObject, _methodPtrAux)] - ThumbEmitLoadRegIndirect(ThumbReg(12), ThumbReg(0), DelegateObject::GetOffsetOfMethodPtrAux()); + ThumbEmitLoadRegIndirect(ThumbReg(12), ThumbReg(0), DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); // Emit the instructions to rewrite the argument registers. Most will be register-to-register (e.g. // move r1 to r0) but one or two of them might move values from the top of the incoming stack @@ -997,7 +997,7 @@ VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) // On entry r0 holds the delegate instance. Look up the real target address stored in the MethodPtrAux // field and stash it in r12. // ldr r12, [r0, #offsetof(DelegateObject, _methodPtrAux)] - ThumbEmitLoadRegIndirect(ThumbReg(12), ThumbReg(0), DelegateObject::GetOffsetOfMethodPtrAux()); + ThumbEmitLoadRegIndirect(ThumbReg(12), ThumbReg(0), DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); // As we copy slots from lower in the argument stack to higher we need to keep track of source and // destination pointers into those arguments (if we just use offsets from SP we get into trouble with diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp index ba9a29ff4bcde5..19a7602c608703 100644 --- a/src/coreclr/vm/arm64/stubs.cpp +++ b/src/coreclr/vm/arm64/stubs.cpp @@ -783,9 +783,9 @@ VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) // On entry x0 holds the delegate instance. Look up the real target address stored in the MethodPtrAux // field and save it in x16(ip). Tailcall to the target method after re-arranging the arguments // ldr x16, [x0, #offsetof(DelegateObject, _methodPtrAux)] - EmitLoadStoreRegImm(eLOAD, IntReg(16), IntReg(0), DelegateObject::GetOffsetOfMethodPtrAux()); - //add x11, x0, DelegateObject::GetOffsetOfMethodPtrAux() - load the indirection cell into x11 used by ResolveWorkerAsmStub - EmitAddImm(IntReg(11), IntReg(0), DelegateObject::GetOffsetOfMethodPtrAux()); + EmitLoadStoreRegImm(eLOAD, IntReg(16), IntReg(0), DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); + //add x11, x0, DelegateObject::GetOffsetOfExtraFunctionPointerOrData() - load the indirection cell into x11 used by ResolveWorkerAsmStub + EmitAddImm(IntReg(11), IntReg(0), DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); for (ShuffleEntry* pEntry = pShuffleEntryArray; pEntry->srcofs != ShuffleEntry::SENTINEL; pEntry++) { diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 24445074500754..98fec92198aeef 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -26,8 +26,8 @@ #include "comcallablewrapper.h" #endif // FEATURE_COMINTEROP -#define DELEGATE_MARKER_UNMANAGEDFPTR -1 - +#define DELEGATE_MARKER_STATICCLOSEDSPECIAL (-1) +#define DELEGATE_MARKER_UNMANAGEDFPTR (-2) #ifndef DACCESS_COMPILE @@ -853,7 +853,7 @@ static Stub* CreateILDelegateShuffleThunk(MethodDesc* pDelegateMD, bool callTarg pCode->EmitLDARG(i); pCode->EmitLoadThis(); - pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__METHOD_PTR_AUX))); + pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__EXTRA_FUNCTION_POINTER_OR_DATA))); pCode->EmitCALLI(TOKEN_ILSTUB_TARGET_SIG, sig.NumFixedArgs(), sig.IsReturnTypeVoid() ? 0 : 1); pCode->EmitRET(); @@ -1219,7 +1219,7 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, // Open delegates use themselves as the target (which handily allows their shuffle thunks to locate additional data at // invocation time). - refRealDelegate->SetTarget(refRealDelegate); + refRealDelegate->SetFirstParameter(refRealDelegate); // We need to shuffle arguments for open delegates since the first argument on the calling side is not meaningful to the // callee. @@ -1227,7 +1227,7 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, PCODE pEntryPoint = SetupShuffleThunk(pDelegateMT, pTargetMethod); // Indicate that the delegate will jump to the shuffle thunk rather than directly to the target method. - refRealDelegate->SetMethodPtr(pEntryPoint); + refRealDelegate->SetFunctionPointer(pEntryPoint); // Use stub dispatch for all virtuals. // Investigate not using this for non-interface virtuals. @@ -1241,7 +1241,7 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, // needs to jump to another stub (this time provided by the VirtualStubManager) that will virtualize the call at // runtime. PCODE pTargetCall = GetVirtualCallStub(pTargetMethod, TypeHandle(pExactMethodType)); - refRealDelegate->SetMethodPtrAux(pTargetCall); + refRealDelegate->SetExtraFunctionPointerOrData(pTargetCall); refRealDelegate->SetInvocationCount((INT_PTR)(void *)pTargetMethod); } else @@ -1267,7 +1267,7 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, // Note that it is important to cache pTargetCode in local variable to avoid GC hole. // GetMultiCallableAddrOfCode() can trigger GC. PCODE pTargetCode = pTargetMethod->GetMultiCallableAddrOfCode(); - refRealDelegate->SetMethodPtrAux(pTargetCode); + refRealDelegate->SetExtraFunctionPointerOrData(pTargetCode); } } else @@ -1298,15 +1298,15 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, } _ASSERTE(pTargetCode); - refRealDelegate->SetTarget(*pRefFirstArg); - refRealDelegate->SetMethodPtr(pTargetCode); + refRealDelegate->SetFirstParameter(*pRefFirstArg); + refRealDelegate->SetFunctionPointer(pTargetCode); } LoaderAllocator *pLoaderAllocator = pTargetMethod->GetLoaderAllocator(); - _ASSERTE(refRealDelegate->GetInvocationList() == NULL); + _ASSERTE(refRealDelegate->GetHelperObject() == NULL); if (pLoaderAllocator->IsCollectible()) - refRealDelegate->SetInvocationList(pLoaderAllocator->GetExposedObject()); + refRealDelegate->SetHelperObject(pLoaderAllocator->GetExposedObject()); GCPROTECT_END(); } @@ -1342,7 +1342,7 @@ LPVOID COMDelegate::ConvertToCallback(OBJECTREF pDelegateObj) // that function pointer. if (DELEGATE_MARKER_UNMANAGEDFPTR == pDelegate->GetInvocationCount()) { - pCode = pDelegate->GetMethodPtrAux(); + pCode = pDelegate->GetExtraFunctionPointerOrData(); } else { @@ -1390,7 +1390,7 @@ LPVOID COMDelegate::ConvertToCallback(OBJECTREF pDelegateObj) _ASSERTE(objhnd != NULL); // This target should not ever be used. We are storing it in the thunk for better diagnostics of "call on collected delegate" crashes. - PCODE pManagedTargetForDiagnostics = (pDelegate->GetMethodPtrAux() != (PCODE)NULL) ? pDelegate->GetMethodPtrAux() : pDelegate->GetMethodPtr(); + PCODE pManagedTargetForDiagnostics = pDelegate->GetExtraFunctionPointerOrData() != (PCODE)NULL ? pDelegate->GetExtraFunctionPointerOrData() : pDelegate->GetFunctionPointer(); // MethodDesc is passed in for profiling to know the method desc of target pUMEntryThunk->LoadTimeInit( @@ -1518,11 +1518,11 @@ OBJECTREF COMDelegate::ConvertToDelegate(LPVOID pCallback, MethodTable* pMT) GCX_NOTRIGGER(); // Wire up the unmanaged call stub to the delegate. - delObj->SetTarget(delObj); // We are the "this" object + delObj->SetFirstParameter(delObj); // We are the "this" object // For X86, we save the entry point in the delegate's method pointer and the UM Callsite in the aux pointer. - delObj->SetMethodPtr(pMarshalStub); - delObj->SetMethodPtrAux((PCODE)pCallback); + delObj->SetFunctionPointer(pMarshalStub); + delObj->SetExtraFunctionPointerOrData((PCODE)pCallback); // Also, mark this delegate as an unmanaged function pointer wrapper. delObj->SetInvocationCount(DELEGATE_MARKER_UNMANAGEDFPTR); @@ -1598,7 +1598,7 @@ extern "C" void QCALLTYPE Delegate_InitializeVirtualCallStub(QCall::ObjectHandle PCODE target = GetVirtualCallStub(pMeth, TypeHandle(pMeth->GetMethodTable())); DELEGATEREF refThis = (DELEGATEREF)d.Get(); - refThis->SetMethodPtrAux(target); + refThis->SetExtraFunctionPointerOrData(target); refThis->SetInvocationCount((INT_PTR)(void*)pMeth); END_QCALL; @@ -1730,30 +1730,30 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q if (COMDelegate::NeedsWrapperDelegate(pMeth)) refThis = COMDelegate::CreateWrapperDelegate(refThis, pMeth); - _ASSERTE(refThis->GetInvocationList() == NULL); + _ASSERTE(refThis->GetHelperObject() == NULL); if (pMeth->GetLoaderAllocator()->IsCollectible()) - refThis->SetInvocationList(pMeth->GetLoaderAllocator()->GetExposedObject()); + refThis->SetHelperObject(pMeth->GetLoaderAllocator()->GetExposedObject()); // Open delegates. if (invokeArgCount == methodArgCount) { // set the target - refThis->SetTarget(refThis); + refThis->SetFirstParameter(refThis); // set the shuffle thunk PCODE pEntryPoint = SetupShuffleThunk(pDelMT, pMeth); - refThis->SetMethodPtr(pEntryPoint); + refThis->SetFunctionPointer(pEntryPoint); // set the ptr aux according to what is needed, if virtual need to call make virtual stub dispatch if (!pMeth->IsStatic() && pMeth->IsVirtual() && !pMeth->GetMethodTable()->IsValueType()) { PCODE pTargetCall = GetVirtualCallStub(pMeth, TypeHandle(pMeth->GetMethodTable())); - refThis->SetMethodPtrAux(pTargetCall); + refThis->SetExtraFunctionPointerOrData(pTargetCall); refThis->SetInvocationCount((INT_PTR)(void *)pMeth); } else { - refThis->SetMethodPtrAux(method); + refThis->SetExtraFunctionPointerOrData(method); } } else @@ -1802,8 +1802,8 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q } #endif // HAS_THISPTR_RETBUF_PRECODE - refThis->SetTarget(target.Get()); - refThis->SetMethodPtr((PCODE)(void *)method); + refThis->SetFirstParameter(target.Get()); + refThis->SetFunctionPointer((PCODE)(void *)method); } GCPROTECT_END(); @@ -1836,7 +1836,6 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) // If you modify this logic, please update cDAC IObject.GetDelegateInfo. DELEGATEREF thisDel = (DELEGATEREF) orDelegate; - DELEGATEREF innerDel = NULL; MethodDesc *pMethodHandle = thisDel->GetMethodDesc(); @@ -1849,16 +1848,15 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) if (count != 0) { // this is one of the following: - // - multicast - _invocationList is Array && _invocationCount != 0 - // - unamanaged ftn ptr - _invocationList == NULL && _invocationCount == -1 - // - wrapper delegate - _invocationList is Delegate && _invocationCount != NULL - // - virtual delegate - _invocationList == null && _invocationCount == (target MethodDesc) - // or _invocationList points to a LoaderAllocator/DynamicResolver (inner open virtual delegate of a Wrapper Delegate) + // - multicast - _helperObject is Array && _invocationCount != 0 + // - unamanaged ftn ptr - _helperObject == NULL && _invocationCount == -1 + // - wrapper delegate - _helperObject is Delegate && _invocationCount != NULL + // - virtual delegate - _helperObject == null && _invocationCount == (target MethodDesc) + // or _helperObject points to a LoaderAllocator/DynamicResolver (inner open virtual delegate of a Wrapper Delegate) // in the wrapper delegate case we want to unwrap and return the method desc of the inner delegate // in the other cases we return the method desc for the invoke - innerDel = (DELEGATEREF) thisDel->GetInvocationList(); bool fOpenVirtualDelegate = false; - + OBJECTREF innerDel = thisDel->GetHelperObject(); if (innerDel != NULL) { MethodTable *pMT = innerDel->GetMethodTable(); @@ -1887,12 +1885,12 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) else { // Next, check for an open delegate - PCODE code = thisDel->GetMethodPtrAux(); + PCODE code = thisDel->GetExtraFunctionPointerOrData(); if (code == (PCODE)NULL) { // Must be a normal delegate - code = thisDel->GetMethodPtr(); + code = thisDel->GetFunctionPointer(); } pMethodHandle = NonVirtualEntry2MethodDesc(code); @@ -1926,10 +1924,10 @@ OBJECTREF COMDelegate::GetTargetObject(OBJECTREF obj) // - multicast // - unmanaged ftn ptr // - wrapper delegate - // - virtual delegate - _invocationList == null && _invocationCount == (target MethodDesc) - // or _invocationList points to a LoaderAllocator/DynamicResolver (inner open virtual delegate of a Wrapper Delegate) + // - virtual delegate - _helperObject == null && _invocationCount == (target MethodDesc) + // or _helperObject points to a LoaderAllocator/DynamicResolver (inner open virtual delegate of a Wrapper Delegate) // in the wrapper delegate case we want to unwrap and return the object of the inner delegate - innerDel = (DELEGATEREF) thisDel->GetInvocationList(); + innerDel = thisDel->GetHelperObject(); if (innerDel != NULL) { MethodTable *pMT = innerDel->GetMethodTable(); @@ -1941,7 +1939,7 @@ OBJECTREF COMDelegate::GetTargetObject(OBJECTREF obj) } if (targetObject == NULL) - targetObject = thisDel->GetTarget(); + targetObject = thisDel->GetFirstParameter(); return targetObject; } @@ -1961,7 +1959,7 @@ BOOL COMDelegate::IsTrueMulticastDelegate(OBJECTREF delegate) size_t invocationCount = ((DELEGATEREF)delegate)->GetInvocationCount(); if (invocationCount) { - OBJECTREF invocationList = ((DELEGATEREF)delegate)->GetInvocationList(); + OBJECTREF invocationList = ((DELEGATEREF)delegate)->GetHelperObject(); if (invocationList != NULL) { MethodTable *pMT = invocationList->GetMethodTable(); @@ -2085,7 +2083,7 @@ BOOL COMDelegate::NeedsWrapperDelegate(MethodDesc* pTargetMD) // to create a wrapper delegate wrapper we need: -// - the delegate to forward to -> _invocationList +// - the delegate to forward to -> _helperObject // - the delegate invoke MethodDesc -> _count // the 2 fields used for invocation will contain: // - the delegate itself -> _pORField @@ -2116,17 +2114,17 @@ DELEGATEREF COMDelegate::CreateWrapperDelegate(DELEGATEREF delegate, MethodDesc* // // Object reference field... - gc.refWrapperDel->SetTarget(gc.refWrapperDel); + gc.refWrapperDel->SetFirstParameter(gc.refWrapperDel); // save the secure invoke stub. GetWrapperInvoke() can trigger GC. PCODE tmp = GetWrapperInvoke(pMD); - gc.refWrapperDel->SetMethodPtr(tmp); + gc.refWrapperDel->SetFunctionPointer(tmp); // save the delegate MethodDesc for the frame gc.refWrapperDel->SetInvocationCount((INT_PTR)pMD); // save the delegate to forward to gc.innerDel = (DELEGATEREF) pDelegateType->Allocate(); - gc.refWrapperDel->SetInvocationList(gc.innerDel); + gc.refWrapperDel->SetHelperObject(gc.innerDel); GCPROTECT_END(); @@ -2244,7 +2242,7 @@ extern "C" PCODE QCALLTYPE Delegate_GetMulticastInvokeSlow(MethodTable* pDelegat // Load next delegate from array using LoopCounter as index pCode->EmitLoadThis(); - pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__MULTICAST_DELEGATE__INVOCATION_LIST))); + pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__HELPER_OBJECT))); pCode->EmitLDLOC(dwLoopCounterNum); pCode->EmitLDELEM_REF(); @@ -2268,7 +2266,7 @@ extern "C" PCODE QCALLTYPE Delegate_GetMulticastInvokeSlow(MethodTable* pDelegat // compare LoopCounter with InvocationCount. If less then branch to nextDelegate pCode->EmitLDLOC(dwLoopCounterNum); pCode->EmitLoadThis(); - pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__MULTICAST_DELEGATE__INVOCATION_COUNT))); + pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__INVOCATION_COUNT))); pCode->EmitBLT(nextDelegate); // load the return value. return value from the last delegate call is returned @@ -2340,7 +2338,7 @@ PCODE COMDelegate::GetWrapperInvoke(MethodDesc* pMD) // Load the "real" delegate pCode->EmitLoadThis(); - pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__MULTICAST_DELEGATE__INVOCATION_LIST))); + pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__HELPER_OBJECT))); // Load the arguments UINT paramCount = 0; @@ -2798,7 +2796,7 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT BOOL isCollectible = pTargetMethodLoaderAllocator->IsCollectible(); // A method that may be instantiated over a collectible type, and is static will require a delegate // that has the LoaderAllocator of the collectible assembly associated with the instantiation - // stored in the _invocationList field. + // stored in the _helperObject field. BOOL fMaybeCollectibleAndStatic = FALSE; if (isStatic) @@ -2862,27 +2860,27 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT // DELEGATE KINDS TABLE // - // _target _methodPtr _methodPtrAux _invocationList _invocationCount + // _helperObject _firstParameter _functionPointer _extraFunctionPointerOrData _invocationCount // - // 1- Instance closed 'this' ptr target method null null 0 - // 2- Instance open non-virt delegate shuffle thunk target method null 0 - // 3- Instance open virtual delegate Virtual-stub dispatch method id null 0 - // 4- Static closed first arg target method null null 0 - // 5- Static closed (special sig) delegate specialSig thunk target method first arg 0 - // 6- Static opened delegate shuffle thunk target method null 0 - // 7- Wrapper delegate call thunk MethodDesc (frame) target delegate (arm only, VSD indirection cell address) + // 1- Instance closed null 'this' ptr target method null 0 + // 2- Instance open non-virt null delegate shuffle thunk target method 0 + // 3- Instance open virtual null delegate Virtual-stub dispatch method id 0 + // 4- Static closed null first arg target method null 0 + // 5- Static closed (special sig) null first arg ThisPtrRetBuf precode null 0 + // 6- Static opened null delegate shuffle thunk target method 0 + // 7- Wrapper target delegate other delegate call thunk MethodDesc (frame) (arm only, VSD indirection cell address) // // Delegate invoke arg count == target method arg count - 2, 3, 6 // Delegate invoke arg count == 1 + target method arg count - 1, 4, 5 // - // 1, 4 - MulticastDelegate.ctor1 (simply assign _target and _methodPtr) + // 1, 4 - MulticastDelegate.ctor1 (simply assign _firstParameter and _functionPointer) // 5 - MulticastDelegate.ctor2 (see table, takes 3 args) // 2, 6 - MulticastDelegate.ctor3 (take shuffle thunk) // 3 - MulticastDelegate.ctor4 (take shuffle thunk, retrieve MethodDesc) ??? // // 7 - Needs special handling // - // With collectible types, we need to fill the _invocationList field in with a value that represents the LoaderAllocator of the target method + // With collectible types, we need to fill the _helperObject field in with a value that represents the LoaderAllocator of the target method // if the delegate is not a closed instance delegate. // // There are two techniques that will work for this. @@ -2978,10 +2976,9 @@ BOOL COMDelegate::IsWrapperDelegate(DELEGATEREF dRef) GC_NOTRIGGER; } CONTRACTL_END; - DELEGATEREF innerDel = NULL; if (dRef->GetInvocationCount() != 0) { - innerDel = (DELEGATEREF) dRef->GetInvocationList(); + OBJECTREF innerDel = dRef->GetHelperObject(); if (innerDel != NULL && innerDel->GetMethodTable()->IsDelegate()) { // We have a wrapper delegate diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index a6e0810fa1d90b..dc7a5ff42e67bb 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -227,17 +227,21 @@ DEFINE_METHOD(DATE_TIME, LONG_CTOR, .ctor, DEFINE_CLASS(DECIMAL, System, Decimal) DEFINE_METHOD(DECIMAL, CURRENCY_CTOR, .ctor, IM_Currency_RetVoid) -DEFINE_CLASS_U(System, Delegate, NoClass) -DEFINE_FIELD_U(_target, DelegateObject, _target) -DEFINE_FIELD_U(_methodDesc, DelegateObject, _methodDesc) -DEFINE_FIELD_U(_methodPtr, DelegateObject, _methodPtr) -DEFINE_FIELD_U(_methodPtrAux, DelegateObject, _methodPtrAux) -DEFINE_CLASS(DELEGATE, System, Delegate) -DEFINE_FIELD(DELEGATE, TARGET, _target) -DEFINE_FIELD(DELEGATE, METHOD_PTR, _methodPtr) -DEFINE_FIELD(DELEGATE, METHOD_PTR_AUX, _methodPtrAux) -DEFINE_METHOD(DELEGATE, CONSTRUCT_DELEGATE, DelegateConstruct, IM_Obj_IntPtr_RetVoid) -DEFINE_METHOD(DELEGATE, GET_INVOKE_METHOD, GetInvokeMethod, IM_RetIntPtr) +DEFINE_CLASS_U(System, Delegate, NoClass) +DEFINE_FIELD_U(_helperObject, DelegateObject, _helperObject) +DEFINE_FIELD_U(_firstParameter, DelegateObject, _firstParameter) +DEFINE_FIELD_U(_functionPointer, DelegateObject, _functionPointer) +DEFINE_FIELD_U(_extraFunctionPointerOrData, DelegateObject, _extraFunctionPointerOrData) +DEFINE_FIELD_U(_invocationCount, DelegateObject, _invocationCount) +DEFINE_FIELD_U(_methodDesc, DelegateObject, _methodDesc) +DEFINE_CLASS(DELEGATE, System, Delegate) +DEFINE_FIELD(DELEGATE, HELPER_OBJECT, _helperObject) +DEFINE_FIELD(DELEGATE, FIRST_PARAMETER, _firstParameter) +DEFINE_FIELD(DELEGATE, FUNCTION_POINTER, _functionPointer) +DEFINE_FIELD(DELEGATE, EXTRA_FUNCTION_POINTER_OR_DATA, _extraFunctionPointerOrData) +DEFINE_FIELD(DELEGATE, INVOCATION_COUNT, _invocationCount) +DEFINE_METHOD(DELEGATE, CONSTRUCT_DELEGATE, DelegateConstruct, IM_Obj_IntPtr_RetVoid) +DEFINE_METHOD(DELEGATE, GET_INVOKE_METHOD, GetInvokeMethod, IM_RetIntPtr) DEFINE_CLASS(INT128, System, Int128) DEFINE_CLASS(UINT128, System, UInt128) @@ -584,12 +588,7 @@ DEFINE_CLASS(MODULE, Reflection, RuntimeModule) DEFINE_CLASS(TYPE_BUILDER, ReflectionEmit, TypeBuilder) DEFINE_CLASS(ENUM_BUILDER, ReflectionEmit, EnumBuilder) -DEFINE_CLASS_U(System, MulticastDelegate, DelegateObject) -DEFINE_FIELD_U(_invocationList, DelegateObject, _invocationList) -DEFINE_FIELD_U(_invocationCount, DelegateObject, _invocationCount) DEFINE_CLASS(MULTICAST_DELEGATE, System, MulticastDelegate) -DEFINE_FIELD(MULTICAST_DELEGATE, INVOCATION_LIST, _invocationList) -DEFINE_FIELD(MULTICAST_DELEGATE, INVOCATION_COUNT, _invocationCount) DEFINE_METHOD(MULTICAST_DELEGATE, CTOR_CLOSED, CtorClosed, IM_Obj_IntPtr_RetVoid) DEFINE_METHOD(MULTICAST_DELEGATE, CTOR_CLOSED_STATIC, CtorClosedStatic, IM_Obj_IntPtr_RetVoid) DEFINE_METHOD(MULTICAST_DELEGATE, CTOR_RT_CLOSED, CtorRTClosed, IM_Obj_IntPtr_RetVoid) diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index 830cc01e966fa1..7315c171a2d277 100644 --- a/src/coreclr/vm/dllimport.cpp +++ b/src/coreclr/vm/dllimport.cpp @@ -2207,7 +2207,7 @@ void PInvokeStubLinker::Begin(DWORD dwStubFlags) m_pcsDispatch->EmitADD(); m_pcsDispatch->EmitLDIND_I(); // get OBJECTHANDLE m_pcsDispatch->EmitLDIND_REF(); // get Delegate object - m_pcsDispatch->EmitLDFLD(GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__TARGET))); + m_pcsDispatch->EmitLDFLD(GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__HELPER_OBJECT))); #endif // FEATURE_PORTABLE_ENTRYPOINTS } } @@ -2407,7 +2407,7 @@ void PInvokeStubLinker::DoPInvoke(ILCodeStream *pcsEmit, DWORD dwStubFlags, Meth #else // !FEATURE_PORTABLE_ENTRYPOINTS if (SF_IsDelegateStub(dwStubFlags)) // reverse P/Invoke via delegate { - int tokDelegate_methodPtr = pcsEmit->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__METHOD_PTR)); + int tokDelegate_methodPtr = pcsEmit->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__FUNCTION_POINTER)); EmitLoadStubContext(pcsEmit, dwStubFlags); pcsEmit->EmitLDC(offsetof(UMEntryThunkData, m_pObjectHandle)); diff --git a/src/coreclr/vm/dllimportcallback.h b/src/coreclr/vm/dllimportcallback.h index da140e3f8aedcf..83e6dc8d11e730 100644 --- a/src/coreclr/vm/dllimportcallback.h +++ b/src/coreclr/vm/dllimportcallback.h @@ -308,7 +308,7 @@ class UMEntryThunkData // We have optimizations that skip the Invoke method and call directly the // delegate's target method. We need to return the target in that case, // otherwise debugger would fail to step in. - RETURN orDelegate->GetMethodPtr(); + RETURN orDelegate->GetFunctionPointer(); } else if (m_pManagedTarget != (PCODE)NULL) { diff --git a/src/coreclr/vm/i386/stublinkerx86.cpp b/src/coreclr/vm/i386/stublinkerx86.cpp index 3c37645e2cd66b..57fdbc0452a3a3 100644 --- a/src/coreclr/vm/i386/stublinkerx86.cpp +++ b/src/coreclr/vm/i386/stublinkerx86.cpp @@ -1194,9 +1194,9 @@ VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) } // mov r10, [r11 + Delegate._methodptraux] - X86EmitIndexRegLoad(kR10, kR11, DelegateObject::GetOffsetOfMethodPtrAux()); - // add r11, DelegateObject::GetOffsetOfMethodPtrAux() - load the indirection cell into r11 - X86EmitAddReg(kR11, DelegateObject::GetOffsetOfMethodPtrAux()); + X86EmitIndexRegLoad(kR10, kR11, DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); + // add r11, DelegateObject::GetOffsetOfExtraFunctionPointerOrData() - load the indirection cell into r11 + X86EmitAddReg(kR11, DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); // Now jump to real target // jmp r10 static const BYTE bjmpr10[] = { 0x41, 0xff, 0xe2 }; @@ -1287,7 +1287,7 @@ VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) // Now jump to real target // JMP [SCRATCHREG] // we need to jump indirect so that for virtual delegates eax contains a pointer to the indirection cell - X86EmitAddReg(SCRATCH_REGISTER_X86REG, DelegateObject::GetOffsetOfMethodPtrAux()); + X86EmitAddReg(SCRATCH_REGISTER_X86REG, DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); static const BYTE bjmpeax[] = { 0xff, 0x20 }; EmitBytes(bjmpeax, sizeof(bjmpeax)); diff --git a/src/coreclr/vm/i386/virtualcallstubcpu.hpp b/src/coreclr/vm/i386/virtualcallstubcpu.hpp index f85c5f557d5ec1..1e133f745a06ad 100644 --- a/src/coreclr/vm/i386/virtualcallstubcpu.hpp +++ b/src/coreclr/vm/i386/virtualcallstubcpu.hpp @@ -457,7 +457,7 @@ BOOL isDelegateCall(BYTE *interiorPtr) if (GCHeapUtilities::GetGCHeap()->IsHeapPointer((void*)interiorPtr)) { - Object *delegate = (Object*)(interiorPtr - DelegateObject::GetOffsetOfMethodPtrAux()); + Object *delegate = (Object*)(interiorPtr - DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); VALIDATEOBJECTREF(ObjectToOBJECTREF(delegate)); _ASSERTE(delegate->GetMethodTable()->IsDelegate()); diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 7ef6d91d3506ed..4aa986869b3aba 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -3305,7 +3305,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr DELEGATEREF* delegateObj = LOCAL_VAR_ADDR(callArgsOffset, DELEGATEREF); NULL_CHECK(*delegateObj); - PCODE targetAddress = (*delegateObj)->GetMethodPtr(); + PCODE targetAddress = (*delegateObj)->GetFunctionPointer(); DelegateEEClass *pDelClass = (DelegateEEClass*)(*delegateObj)->GetMethodTable()->GetClass(); if ((pDelClass->m_pInstRetBuffCallStub != NULL && pDelClass->m_pInstRetBuffCallStub->GetEntryPoint() == targetAddress) || (pDelClass->m_pStaticCallStub != NULL && pDelClass->m_pStaticCallStub->GetEntryPoint() == targetAddress)) @@ -3313,7 +3313,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr // This implies that we're using a delegate shuffle thunk to strip off the first parameter to the method // and call the actual underlying method. We allow for tail-calls to work and for greater efficiency in the // interpreter by skipping the shuffle thunk and calling the actual target method directly. - PCODE actualTarget = (*delegateObj)->GetMethodPtrAux(); + PCODE actualTarget = (*delegateObj)->GetExtraFunctionPointerOrData(); // Detect open virtual dispatch scenario bool isOpenVirtual = false; @@ -3390,7 +3390,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } } - OBJECTREF targetMethodObj = (*delegateObj)->GetTarget(); + OBJECTREF targetMethodObj = (*delegateObj)->GetFirstParameter(); LOCAL_VAR(callArgsOffset, OBJECTREF) = targetMethodObj; if ((targetMethod = NonVirtualEntry2MethodDesc(targetAddress)) != NULL) diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index a71a92fc5519e4..34395ee0ed6f7d 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1903,9 +1903,9 @@ HCIMPL2(void, JIT_DelegateProfile32, Object *obj, ICorJitInfo::HandleHistogram32 // MethodDesc* pRecordedMD = (MethodDesc*)DEFAULT_UNKNOWN_HANDLE; DELEGATEREF del = (DELEGATEREF)objRef; - if ((del->GetInvocationCount() == 0) && (del->GetMethodPtrAux() == (PCODE)NULL)) + if ((del->GetInvocationCount() == 0) && (del->GetExtraFunctionPointerOrData() == (PCODE)NULL)) { - MethodDesc* pMD = NonVirtualEntry2MethodDesc(del->GetMethodPtr()); + MethodDesc* pMD = NonVirtualEntry2MethodDesc(del->GetFunctionPointer()); if ((pMD != nullptr) && !pMD->GetLoaderAllocator()->IsCollectible() && !pMD->IsDynamicMethod()) { pRecordedMD = pMD; @@ -1949,9 +1949,9 @@ HCIMPL2(void, JIT_DelegateProfile64, Object *obj, ICorJitInfo::HandleHistogram64 // MethodDesc* pRecordedMD = (MethodDesc*)DEFAULT_UNKNOWN_HANDLE; DELEGATEREF del = (DELEGATEREF)objRef; - if ((del->GetInvocationCount() == 0) && (del->GetMethodPtrAux() == (PCODE)NULL)) + if ((del->GetInvocationCount() == 0) && (del->GetExtraFunctionPointerOrData() == (PCODE)NULL)) { - MethodDesc* pMD = NonVirtualEntry2MethodDesc(del->GetMethodPtr()); + MethodDesc* pMD = NonVirtualEntry2MethodDesc(del->GetFunctionPointer()); if ((pMD != nullptr) && !pMD->GetLoaderAllocator()->IsCollectible() && !pMD->IsDynamicMethod()) { pRecordedMD = pMD; diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index c0ec22f3e75a8f..153da51e71eed9 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -10324,18 +10324,18 @@ void CEEInfo::getEEInfo(CORINFO_EE_INFO *pEEInfoOut) #ifndef CROSSBITNESS_COMPILE // The assertions must hold in every non-crossbitness scenario - _ASSERTE(OFFSETOF__DelegateObject__target == DelegateObject::GetOffsetOfTarget()); - _ASSERTE(OFFSETOF__DelegateObject__methodPtr == DelegateObject::GetOffsetOfMethodPtr()); - _ASSERTE(OFFSETOF__DelegateObject__methodPtrAux == DelegateObject::GetOffsetOfMethodPtrAux()); - _ASSERTE(OFFSETOF__PtrArray__m_Array_ == PtrArray::GetDataOffset()); + _ASSERTE(OFFSETOF__DelegateObject__firstParameter == DelegateObject::GetOffsetOfFirstParameter()); + _ASSERTE(OFFSETOF__DelegateObject__functionPointer == DelegateObject::GetOffsetOfFunctionPointer()); + _ASSERTE(OFFSETOF__DelegateObject__extraFunctionPointerOrData == DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); + _ASSERTE(OFFSETOF__PtrArray__m_Array_ == PtrArray::GetDataOffset()); #endif // Delegate offsets - pEEInfoOut->offsetOfDelegateInstance = OFFSETOF__DelegateObject__target; - pEEInfoOut->offsetOfDelegateFirstTarget = OFFSETOF__DelegateObject__methodPtr; + pEEInfoOut->offsetOfDelegateInstance = OFFSETOF__DelegateObject__firstParameter; + pEEInfoOut->offsetOfDelegateFirstTarget = OFFSETOF__DelegateObject__functionPointer; // Wrapper delegate offsets - pEEInfoOut->offsetOfWrapperDelegateIndirectCell = OFFSETOF__DelegateObject__methodPtrAux; + pEEInfoOut->offsetOfWrapperDelegateIndirectCell = OFFSETOF__DelegateObject__extraFunctionPointerOrData; pEEInfoOut->sizeOfReversePInvokeFrame = TARGET_POINTER_SIZE * READYTORUN_ReversePInvokeTransitionFrameSizeInPointerUnits; diff --git a/src/coreclr/vm/loongarch64/stubs.cpp b/src/coreclr/vm/loongarch64/stubs.cpp index fc5a820e0aca07..1027f90cc4c339 100644 --- a/src/coreclr/vm/loongarch64/stubs.cpp +++ b/src/coreclr/vm/loongarch64/stubs.cpp @@ -811,9 +811,9 @@ VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) // On entry a0 holds the delegate instance. Look up the real target address stored in the MethodPtrAux // field and saved in $r21. Tailcall to the target method after re-arranging the arguments // ld.d $r21, $r4, offsetof(DelegateObject, _methodPtrAux) - EmitLoadStoreRegImm(eLOAD, IntReg(21)/*$r21*/, IntReg(4)/*$a0*/, DelegateObject::GetOffsetOfMethodPtrAux()); - // addi.d t8, a0, DelegateObject::GetOffsetOfMethodPtrAux() - load the indirection cell into t8 used by ResolveWorkerAsmStub - EmitAddImm(20/*$t8*/, 4, DelegateObject::GetOffsetOfMethodPtrAux()); + EmitLoadStoreRegImm(eLOAD, IntReg(21)/*$r21*/, IntReg(4)/*$a0*/, DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); + // addi.d t8, a0, DelegateObject::GetOffsetOfExtraFunctionPointerOrData() - load the indirection cell into t8 used by ResolveWorkerAsmStub + EmitAddImm(20/*$t8*/, 4, DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); const ShuffleEntry* entry = pShuffleEntryArray; // Shuffle integer argument registers diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index a82f6b39d22dad..aa01464ead5cd2 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -1744,7 +1744,7 @@ typedef BStrWrapper* BSTRWRAPPEROBJECTREF; #endif // FEATURE_COMINTEROP -// This class corresponds to System.MulticastDelegate on the managed side. +// This class corresponds to System.Delegate on the managed side. class DelegateObject : public Object { friend class CheckAsmOffsets; @@ -1752,58 +1752,56 @@ class DelegateObject : public Object friend struct ::cdac_data; public: - BOOL IsWrapperDelegate() { LIMITED_METHOD_CONTRACT; return _methodPtrAux == 0; } + BOOL IsWrapperDelegate() { LIMITED_METHOD_CONTRACT; return _extraFunctionPointerOrData == 0; } - OBJECTREF GetTarget() { LIMITED_METHOD_CONTRACT; return _target; } - void SetTarget(OBJECTREF target) { WRAPPER_NO_CONTRACT; SetObjectReference(&_target, target); } - static int GetOffsetOfTarget() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _target); } + OBJECTREF GetHelperObject() { LIMITED_METHOD_CONTRACT; return _helperObject; } + void SetHelperObject(OBJECTREF helperObject) { WRAPPER_NO_CONTRACT; SetObjectReference(&_helperObject, helperObject); } + static int GetOffsetOfHelperObject() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _helperObject); } - MethodDesc* GetMethodDesc() { LIMITED_METHOD_CONTRACT; return _methodDesc; } - void SetMethodDesc(MethodDesc* methodDesc) { LIMITED_METHOD_CONTRACT; _methodDesc = methodDesc; } - static int GetOffsetOfMethodDesc() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodDesc); } + OBJECTREF GetFirstParameter() { LIMITED_METHOD_CONTRACT; return _firstParameter; } + void SetFirstParameter(OBJECTREF firstParameter) { WRAPPER_NO_CONTRACT; SetObjectReference(&_firstParameter, firstParameter); } + static int GetOffsetOfFirstParameter() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _firstParameter); } - PCODE GetMethodPtr() { LIMITED_METHOD_CONTRACT; return _methodPtr; } - void SetMethodPtr(PCODE methodPtr) { LIMITED_METHOD_CONTRACT; _methodPtr = methodPtr; } - static int GetOffsetOfMethodPtr() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtr); } + PCODE GetFunctionPointer() { LIMITED_METHOD_CONTRACT; return _functionPointer; } + void SetFunctionPointer(PCODE functionPointer) { LIMITED_METHOD_CONTRACT; _functionPointer = functionPointer; } + static int GetOffsetOfFunctionPointer() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _functionPointer); } - PCODE GetMethodPtrAux() { LIMITED_METHOD_CONTRACT; return _methodPtrAux; } - void SetMethodPtrAux(PCODE methodPtrAux) { LIMITED_METHOD_CONTRACT; _methodPtrAux = methodPtrAux; } - static int GetOffsetOfMethodPtrAux() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtrAux); } - - OBJECTREF GetInvocationList() { LIMITED_METHOD_CONTRACT; return _invocationList; } - void SetInvocationList(OBJECTREF invocationList) { WRAPPER_NO_CONTRACT; SetObjectReference(&_invocationList, invocationList); } - static int GetOffsetOfInvocationList() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _invocationList); } + PCODE GetExtraFunctionPointerOrData() { LIMITED_METHOD_CONTRACT; return _extraFunctionPointerOrData; } + void SetExtraFunctionPointerOrData(PCODE extraFunctionPointerOrData) { LIMITED_METHOD_CONTRACT; _extraFunctionPointerOrData = extraFunctionPointerOrData; } + static int GetOffsetOfExtraFunctionPointerOrData() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _extraFunctionPointerOrData); } INT_PTR GetInvocationCount() { LIMITED_METHOD_CONTRACT; return _invocationCount; } void SetInvocationCount(INT_PTR invocationCount) { LIMITED_METHOD_CONTRACT; _invocationCount = invocationCount; } static int GetOffsetOfInvocationCount() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _invocationCount); } + MethodDesc* GetMethodDesc() { LIMITED_METHOD_CONTRACT; return _methodDesc; } + void SetMethodDesc(MethodDesc* methodDesc) { LIMITED_METHOD_CONTRACT; _methodDesc = methodDesc; } + static int GetOffsetOfMethodDesc() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodDesc); } + // README: // If you modify the order of these fields, make sure to update the definition in // BCL for this object. private: - // System.Delegate - OBJECTREF _methodBase; - OBJECTREF _target; - PCODE _methodPtr; - PCODE _methodPtrAux; - MethodDesc* _methodDesc; - // System.MulticastDelegate - OBJECTREF _invocationList; + OBJECTREF _helperObject; + OBJECTREF _firstParameter; + PCODE _functionPointer; + PCODE _extraFunctionPointerOrData; INT_PTR _invocationCount; + MethodDesc* _methodDesc; }; -#define OFFSETOF__DelegateObject__target (OBJECT_SIZE /* m_pMethTab */ + TARGET_POINTER_SIZE /* _methodBase */) -#define OFFSETOF__DelegateObject__methodPtr (OFFSETOF__DelegateObject__target + TARGET_POINTER_SIZE /* _target */) -#define OFFSETOF__DelegateObject__methodPtrAux (OFFSETOF__DelegateObject__methodPtr + TARGET_POINTER_SIZE /* _methodPtr */) -#define OFFSETOF__DelegateObject__methodDesc (OFFSETOF__DelegateObject__methodPtrAux + TARGET_POINTER_SIZE /* _methodPtrAux */) +#define OFFSETOF__DelegateObject__firstParameter (OBJECT_SIZE /* m_pMethTab */ + TARGET_POINTER_SIZE /* _helperObject */) +#define OFFSETOF__DelegateObject__functionPointer (OFFSETOF__DelegateObject__firstParameter + TARGET_POINTER_SIZE /* _firstParameter */) +#define OFFSETOF__DelegateObject__extraFunctionPointerOrData (OFFSETOF__DelegateObject__functionPointer + TARGET_POINTER_SIZE /* _functionPointer */) +#define OFFSETOF__DelegateObject__methodDesc (OFFSETOF__DelegateObject__extraFunctionPointerOrData + TARGET_POINTER_SIZE /* _extraFunctionPointerOrData */ + \ + TARGET_POINTER_SIZE /* _invocationCount */) template<> struct cdac_data { - static constexpr size_t Target = offsetof(DelegateObject, _target); - static constexpr size_t MethodPtr = offsetof(DelegateObject, _methodPtr); - static constexpr size_t MethodPtrAux = offsetof(DelegateObject, _methodPtrAux); + static constexpr size_t Target = offsetof(DelegateObject, _firstParameter); + static constexpr size_t MethodPtr = offsetof(DelegateObject, _functionPointer); + static constexpr size_t MethodPtrAux = offsetof(DelegateObject, _extraFunctionPointerOrData); static constexpr size_t InvocationCount = offsetof(DelegateObject, _invocationCount); }; diff --git a/src/coreclr/vm/object.h.bak b/src/coreclr/vm/object.h.bak new file mode 100644 index 00000000000000..89a1de721ae0d4 --- /dev/null +++ b/src/coreclr/vm/object.h.bak @@ -0,0 +1,2586 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// OBJECT.H +// +// Definitions of a CLR Object +// + +// See code:EEStartup#TableOfContents for overview + + +#ifndef _OBJECT_H_ +#define _OBJECT_H_ + +#include "util.hpp" +#include "syncblk.h" +#include "gcdesc.h" +#include "sstring.h" +#include "daccess.h" +#include "fcall.h" + +extern "C" void __fastcall ZeroMemoryInGCHeap(void*, size_t); + +void ErectWriteBarrierForMT(MethodTable **dst, MethodTable *ref); + +/* + #ObjectModel + * CLR Internal Object Model + * + * + * Object - This is the common base part to all CLR objects + * | it contains the MethodTable pointer and the + * | sync block index, which is at a negative offset + * | + * +-- StringObject - String objects are specialized objects for string + * | storage/retrieval for higher performance (UCS-2 / UTF-16 data) + * | + * +-- ReflectClassBaseObject - The base object for the RuntimeType class + * | + * +-- ReflectMethodObject - The base object for the RuntimeMethodInfo class + * | + * +-- ReflectFieldObject - The base object for the RtFieldInfo class + * | + * +-- ArrayBase - Base portion of all arrays + * | | + * | +- I1Array - Base type SZ arrays + * | | I2Array + * | | ... + * | | + * | +- PtrArray - SZ Array of OBJECTREFs, different than base arrays because of pObjectClass + * | + * +-- AssemblyBaseObject - The base object for the class Assembly + * | + * | ... + * + * + * PLEASE NOTE THE FOLLOWING WHEN ADDING A NEW OBJECT TYPE: + * + * The size of the object in the heap must be able to be computed + * very, very quickly for GC purposes. Restrictions on the layout + * of the object guarantee this is possible. + * + * Any object that inherits from Object must be able to + * compute its complete size by using the first 4 bytes of + * the object following the Object part and constants + * reachable from the MethodTable... + * + * The formula used for this calculation is: + * MT->GetBaseSize() + ((OBJECTTYPEREF->GetSizeField() * MT->GetComponentSize()) + * + * So for Object, since this is of fixed size, the ComponentSize is 0, which makes the right side + * of the equation above equal to 0 no matter what the value of GetSizeField(), so the size is just the base size. + * + */ + +class MethodTable; +class Thread; +class Assembly; +class AssemblyNative; +class WaitHandleNative; +class ArgDestination; + +struct RCW; + +#ifdef TARGET_64BIT +#define OBJHEADER_SIZE (sizeof(DWORD) /* m_alignpad */ + sizeof(DWORD) /* m_SyncBlockValue */) +#else +#define OBJHEADER_SIZE sizeof(DWORD) /* m_SyncBlockValue */ +#endif + +#define OBJECT_SIZE TARGET_POINTER_SIZE /* m_pMethTab */ +#define OBJECT_BASESIZE (OBJHEADER_SIZE + OBJECT_SIZE) + +#ifdef TARGET_64BIT +#define ARRAYBASE_SIZE (OBJECT_SIZE /* m_pMethTab */ + sizeof(DWORD) /* m_NumComponents */ + sizeof(DWORD) /* pad */) +#else +#define ARRAYBASE_SIZE (OBJECT_SIZE /* m_pMethTab */ + sizeof(DWORD) /* m_NumComponents */) +#endif + +#define ARRAYBASE_BASESIZE (OBJHEADER_SIZE + ARRAYBASE_SIZE) + +// +// The generational GC requires that every object be at least 12 bytes +// in size. + +#define MIN_OBJECT_SIZE (2*TARGET_POINTER_SIZE + OBJHEADER_SIZE) + +#define PTRALIGNCONST (DATA_ALIGNMENT-1) + +#ifndef PtrAlign +#define PtrAlign(size) \ + (((size) + PTRALIGNCONST) & (~PTRALIGNCONST)) +#endif //!PtrAlign + +// code:Object is the representation of an managed object on the GC heap. +// +// See code:#ObjectModel for some important subclasses of code:Object +// +// The only fields mandated by all objects are +// +// * a pointer to the code:MethodTable at offset 0 +// * a pointer to a code:ObjHeader at a negative offset. This is often zero. It holds information that +// any addition information that we might need to attach to arbitrary objects. +// +class Object +{ + friend class CheckAsmOffsets; + + protected: + PTR_MethodTable m_pMethTab; + + protected: + Object() { LIMITED_METHOD_CONTRACT; }; + ~Object() { LIMITED_METHOD_CONTRACT; }; + + public: + MethodTable *RawGetMethodTable() const + { + return m_pMethTab; + } + +#ifndef DACCESS_COMPILE + void RawSetMethodTable(MethodTable *pMT) + { + LIMITED_METHOD_CONTRACT; + m_pMethTab = pMT; + } + + VOID SetMethodTable(MethodTable *pMT) + { + WRAPPER_NO_CONTRACT; + RawSetMethodTable(pMT); + } + + VOID SetMethodTableForUOHObject(MethodTable *pMT) + { + WRAPPER_NO_CONTRACT; + // This function must be used if the allocation occurs on a UOH heap, and the method table might be a collectible type + ErectWriteBarrierForMT(&m_pMethTab, pMT); + } +#endif //!DACCESS_COMPILE + +#define MARKED_BIT 0x1 + + PTR_MethodTable GetMethodTable() const + { + LIMITED_METHOD_DAC_CONTRACT; + +#ifndef DACCESS_COMPILE + // We should always use GetGCSafeMethodTable() if we're running during a GC. + // If the mark bit is set then we're running during a GC + _ASSERTE((dac_cast(m_pMethTab) & MARKED_BIT) == 0); + + return m_pMethTab; +#else //DACCESS_COMPILE + + //@dbgtodo dharvey Make this a type which supports bitwise and operations + //when available + return PTR_MethodTable((dac_cast(m_pMethTab)) & (~MARKED_BIT)); +#endif //DACCESS_COMPILE + } + + DPTR(PTR_MethodTable) GetMethodTablePtr() const + { + LIMITED_METHOD_CONTRACT; + return dac_cast(PTR_HOST_MEMBER_TADDR(Object, this, m_pMethTab)); + } + + TypeHandle GetTypeHandle(); + + // Methods used to determine if an object supports a given interface. + static BOOL SupportsInterface(OBJECTREF pObj, MethodTable *pInterfaceMT); + + inline DWORD GetNumComponents(); + inline SIZE_T GetSize(); + + CGCDesc* GetSlotMap() + { + WRAPPER_NO_CONTRACT; + return( CGCDesc::GetCGCDescFromMT(GetMethodTable())); + } + + // Sync Block & Synchronization services + + // Access the ObjHeader which is at a negative offset on the object (because of + // cache lines) + PTR_ObjHeader GetHeader() + { + LIMITED_METHOD_DAC_CONTRACT; + return dac_cast(this) - 1; + } + + // Get the current address of the object (works for debug refs, too.) + PTR_BYTE GetAddress() + { + LIMITED_METHOD_DAC_CONTRACT; + return dac_cast(this); + } + +#ifdef _DEBUG + // TRUE if the header has a real SyncBlockIndex (i.e. it has an entry in the + // SyncTable, though it doesn't necessarily have an entry in the SyncBlockCache) + BOOL HasEmptySyncBlockInfo() + { + WRAPPER_NO_CONTRACT; + return GetHeader()->HasEmptySyncBlockInfo(); + } +#endif + + // retrieve or allocate a sync block for this object + SyncBlock *GetSyncBlock() + { + WRAPPER_NO_CONTRACT; + return GetHeader()->GetSyncBlock(); + } + + DWORD GetSyncBlockIndex() + { + WRAPPER_NO_CONTRACT; + return GetHeader()->GetSyncBlockIndex(); + } + + // DO NOT ADD ANY ASSERTS TO THIS METHOD. + // DO NOT USE THIS METHOD. + // Yes folks, for better or worse the debugger pokes supposed object addresses + // to try to see if objects are valid, possibly firing an AccessViolation or worse, + // and then catches the AV and reports a failure to the debug client. This makes + // the debugger slightly more robust should any corrupted object references appear + // in a session. Thus it is "correct" behaviour for this to AV when used with + // an invalid object pointer, and incorrect behaviour for it to + // assert. + BOOL ValidateObjectWithPossibleAV(); + + // Validate an object ref out of the VerifyHeap routine in the GC + void ValidateHeap(BOOL bDeep=TRUE); + + PTR_SyncBlock PassiveGetSyncBlock() + { + LIMITED_METHOD_DAC_CONTRACT; + return GetHeader()->PassiveGetSyncBlock(); + } + + static DWORD ComputeHashCode(); + static DWORD GetGlobalNewHashCode(); + + inline INT32 TryGetHashCode(); +#ifndef DACCESS_COMPILE + INT32 GetHashCodeEx(); +#endif // #ifndef DACCESS_COMPILE + + PTR_VOID UnBox(); // if it is a value class, get the pointer to the first field + + PTR_BYTE GetData(void) + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + return dac_cast(this) + sizeof(Object); + } + + static UINT GetOffsetOfFirstField() + { + LIMITED_METHOD_CONTRACT; + return sizeof(Object); + } + + DWORD GetOffset32(DWORD dwOffset) + { + WRAPPER_NO_CONTRACT; + return * PTR_DWORD(GetData() + dwOffset); + } + + USHORT GetOffset16(DWORD dwOffset) + { + WRAPPER_NO_CONTRACT; + return * PTR_USHORT(GetData() + dwOffset); + } + + BYTE GetOffset8(DWORD dwOffset) + { + WRAPPER_NO_CONTRACT; + return * PTR_BYTE(GetData() + dwOffset); + } + + int64_t GetOffset64(DWORD dwOffset) + { + WRAPPER_NO_CONTRACT; + return (int64_t) * PTR_ULONG64(GetData() + dwOffset); + } + + void *GetPtrOffset(DWORD dwOffset) + { + WRAPPER_NO_CONTRACT; + return (void *)(TADDR)*PTR_TADDR(GetData() + dwOffset); + } + +#ifndef DACCESS_COMPILE + + void SetOffsetObjectRef(DWORD dwOffset, size_t dwValue); + + void SetOffsetPtr(DWORD dwOffset, LPVOID value) + { + WRAPPER_NO_CONTRACT; + *(LPVOID *) &GetData()[dwOffset] = value; + } + + void SetOffset32(DWORD dwOffset, DWORD dwValue) + { + WRAPPER_NO_CONTRACT; + *(DWORD *) &GetData()[dwOffset] = dwValue; + } + + void SetOffset16(DWORD dwOffset, DWORD dwValue) + { + WRAPPER_NO_CONTRACT; + *(USHORT *) &GetData()[dwOffset] = (USHORT) dwValue; + } + + void SetOffset8(DWORD dwOffset, DWORD dwValue) + { + WRAPPER_NO_CONTRACT; + *(BYTE *) &GetData()[dwOffset] = (BYTE) dwValue; + } + + void SetOffset64(DWORD dwOffset, int64_t qwValue) + { + WRAPPER_NO_CONTRACT; + *(int64_t *) &GetData()[dwOffset] = qwValue; + } + +#endif // #ifndef DACCESS_COMPILE + + VOID Validate(BOOL bDeep = TRUE, BOOL bVerifyNextHeader = TRUE, BOOL bVerifySyncBlock = TRUE); + + PTR_MethodTable GetGCSafeMethodTable() const + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + + // lose GC marking bit and the reserved bit + // A method table pointer should always be aligned. During GC we set the least + // significant bit for marked objects, and the second to least significant + // bit is reserved. So if we want the actual MT pointer during a GC + // we must zero out the lowest 2 bits on 32-bit and 3 bits on 64-bit. +#ifdef TARGET_64BIT + return dac_cast((dac_cast(m_pMethTab)) & ~((UINT_PTR)7)); +#else + return dac_cast((dac_cast(m_pMethTab)) & ~((UINT_PTR)3)); +#endif //TARGET_64BIT + } + + // There are some cases where it is unsafe to get the type handle during a GC. + // This occurs when the type has already been unloaded as part of an in-progress appdomain shutdown. + TypeHandle GetGCSafeTypeHandleIfPossible() const; + + inline TypeHandle GetGCSafeTypeHandle() const; + +#ifdef DACCESS_COMPILE + void EnumMemoryRegions(void); +#endif + + private: + VOID ValidateInner(BOOL bDeep, BOOL bVerifyNextHeader, BOOL bVerifySyncBlock); + + friend struct ::cdac_data; +}; + +template<> +struct cdac_data +{ + static constexpr size_t m_pMethTab = offsetof(Object, m_pMethTab); +}; + +/* + * Object ref setting routines. You must use these to do + * proper write barrier support. + */ + +// SetObjectReference sets an OBJECTREF field + +void SetObjectReferenceUnchecked(OBJECTREF *dst,OBJECTREF ref); + +#ifdef _DEBUG +void EnableStressHeapHelper(); +#endif + +//Used to clear the object reference +inline void ClearObjectReference(OBJECTREF* dst) +{ + LIMITED_METHOD_CONTRACT; + *(void**)(dst) = NULL; +} + +// CopyValueClass sets a value class field + +void CopyValueClassUnchecked(void* dest, void* src, MethodTable *pMT); +void CopyValueClassArgUnchecked(ArgDestination *argDest, void* src, MethodTable *pMT, int destOffset); + +inline void InitValueClass(void *dest, MethodTable *pMT) +{ + WRAPPER_NO_CONTRACT; + ZeroMemoryInGCHeap(dest, pMT->GetNumInstanceFieldBytes()); +} + +// Initialize value class argument +void InitValueClassArg(ArgDestination *argDest, MethodTable *pMT); + +#define SetObjectReference(_d,_r) SetObjectReferenceUnchecked(_d, _r) +#define CopyValueClass(_d,_s,_m) CopyValueClassUnchecked(_d,_s,_m) +#define CopyValueClassArg(_d,_s,_m,_o) CopyValueClassArgUnchecked(_d,_s,_m,_o) + +#include + + +// There are two basic kinds of array layouts in CLR +// ELEMENT_TYPE_ARRAY - a multidimensional array with lower bounds on the dims +// ELMENNT_TYPE_SZARRAY - A zero based single dimensional array +// +// In addition the layout of an array in memory is also affected by +// whether the method table is shared (eg in the case of arrays of object refs) +// or not. In the shared case, the array has to hold the type handle of +// the element type. +// +// ArrayBase encapuslates all of these details. In theory you should never +// have to peek inside this abstraction +// +class ArrayBase : public Object +{ + friend class GCHeap; + friend class CObjectHeader; + friend class Object; + friend OBJECTREF AllocateSzArray(MethodTable *pArrayMT, INT32 length, GC_ALLOC_FLAGS flags); + friend OBJECTREF TryAllocateFrozenSzArray(MethodTable* pArrayMT, INT32 length); + friend class JIT_TrialAlloc; + friend class CheckAsmOffsets; + friend struct _DacGlobals; + +private: + // This MUST be the first field, so that it directly follows Object. This is because + // Object::GetSize() looks at m_NumComponents even though it may not be an array (the + // values is shifted out if not an array, so it's ok). + DWORD m_NumComponents; +#ifdef TARGET_64BIT + DWORD pad; +#endif // TARGET_64BIT + + SVAL_DECL(INT32, s_arrayBoundsZero); // = 0 + + // What comes after this conceputally is: + // INT32 bounds[rank]; The bounds are only present for Multidimensional arrays + // INT32 lowerBounds[rank]; Valid indexes are lowerBounds[i] <= index[i] < lowerBounds[i] + bounds[i] + +public: +#ifndef DACCESS_COMPILE + void SetNumComponents(INT32 length) + { + LIMITED_METHOD_CONTRACT; + m_NumComponents = length; + } +#endif // !DACCESS_COMPILE + + // Get the element type for the array, this works whether the element + // type is stored in the array or not + inline TypeHandle GetArrayElementTypeHandle() const; + + // Get the CorElementType for the elements in the array. Avoids creating a TypeHandle + inline CorElementType GetArrayElementType() const; + + inline unsigned GetRank() const; + + // Total element count for the array + inline DWORD GetNumComponents() const; + + // Get pointer to elements, handles any number of dimensions + PTR_BYTE GetGCSafeDataPtr() const { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; +#ifdef _DEBUG +#ifndef DACCESS_COMPILE + EnableStressHeapHelper(); +#endif +#endif + return dac_cast(this) + + GetDataPtrOffset(GetGCSafeMethodTable()); + } + + PTR_BYTE GetDataPtr() const { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; +#ifdef _DEBUG +#ifndef DACCESS_COMPILE + EnableStressHeapHelper(); +#endif +#endif + PTR_BYTE result = dac_cast(this) + + GetDataPtrOffset(GetMethodTable()); + _ASSERTE(result == GetGCSafeDataPtr()); + return result; + } + + // The component size is actually 16-bit WORD, but this method is returning SIZE_T to ensure + // that SIZE_T is used everywhere for object size computation. It is necessary to support + // objects bigger than 2GB. + SIZE_T GetComponentSize() const { + WRAPPER_NO_CONTRACT; + MethodTable * pMT; + pMT = GetMethodTable(); + _ASSERTE(pMT->HasComponentSize()); + return pMT->RawGetComponentSize(); + } + + // Note that this can be a multidimensional array of rank 1 + // (for example if we had a 1-D array with lower bounds + BOOL IsMultiDimArray() const { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return(GetMethodTable()->IsMultiDimArray()); + } + + // Get pointer to the beginning of the bounds (counts for each dim) + // Works for any array type + PTR_INT32 GetBoundsPtr() const { + WRAPPER_NO_CONTRACT; + MethodTable * pMT = GetMethodTable(); + if (pMT->IsMultiDimArray()) + { + return dac_cast( + dac_cast(this) + sizeof(*this)); + } + else + { + return dac_cast(PTR_HOST_MEMBER_TADDR(ArrayBase, this, + m_NumComponents)); + } + } + + // Works for any array type + PTR_INT32 GetLowerBoundsPtr() const { + WRAPPER_NO_CONTRACT; + if (IsMultiDimArray()) + { + // Lower bounds info is after total bounds info + // and total bounds info has rank elements + return GetBoundsPtr() + GetRank(); + } + else + return dac_cast(GVAL_ADDR(s_arrayBoundsZero)); + } + + static unsigned GetOffsetOfNumComponents() { + LIMITED_METHOD_CONTRACT; + return offsetof(ArrayBase, m_NumComponents); + } + + inline static unsigned GetDataPtrOffset(MethodTable* pMT); + + inline static unsigned GetBoundsOffset(MethodTable* pMT); + inline static unsigned GetLowerBoundsOffset(MethodTable* pMT); + + friend struct ::cdac_data; +}; + +#ifndef DACCESS_COMPILE +template<> +struct cdac_data +{ + static constexpr size_t m_NumComponents = offsetof(ArrayBase, m_NumComponents); + + static constexpr INT32* ArrayBoundsZero = &ArrayBase::s_arrayBoundsZero; +}; +#endif + +// +// Template used to build all the non-object +// arrays of a single dimension +// + +template < class KIND > +class Array : public ArrayBase +{ + public: + + typedef DPTR(KIND) PTR_KIND; + + KIND m_Array[1]; + + PTR_KIND GetDirectPointerToNonObjectElements() const + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return dac_cast(PTR_HOST_MEMBER_TADDR(Array, this, m_Array)); + } +}; + + +// Warning: Use PtrArray only for single dimensional arrays, not multidim arrays. +class PtrArray : public ArrayBase +{ + friend class GCHeap; + friend class ClrDataAccess; + friend class JIT_TrialAlloc; + friend class CheckAsmOffsets; + +public: + TypeHandle GetArrayElementTypeHandle() + { + LIMITED_METHOD_CONTRACT; + return GetMethodTable()->GetArrayElementTypeHandle(); + } + + PTR_OBJECTREF GetDataPtr() + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + return dac_cast(dac_cast(this) + GetDataOffset()); + } + + static SIZE_T GetDataOffset() + { + LIMITED_METHOD_CONTRACT; + return offsetof(PtrArray, m_Array); + } + + void SetAt(SIZE_T i, OBJECTREF ref) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + _ASSERTE(i < GetNumComponents()); + SetObjectReference(m_Array + i, ref); + } + + void ClearAt(SIZE_T i) + { + WRAPPER_NO_CONTRACT; + _ASSERTE(i < GetNumComponents()); + ClearObjectReference(m_Array + i); + } + + OBJECTREF GetAt(SIZE_T i) + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + _ASSERTE(i < GetNumComponents()); + +// DAC doesn't know the true size of this array +// the compiler thinks it is size 1, but really it is size N hanging off the structure +#ifndef DACCESS_COMPILE + return m_Array[i]; +#else + TADDR arrayTargetAddress = dac_cast(this) + offsetof(PtrArray, m_Array); + __ArrayDPtr targetArray = dac_cast< __ArrayDPtr >(arrayTargetAddress); + return targetArray[i]; +#endif + } + + friend class StubLinkerCPU; + friend class ArrayOpLinker; +public: + OBJECTREF m_Array[1]; +}; + +#define OFFSETOF__PtrArray__m_Array_ ARRAYBASE_SIZE + +/* a TypedByRef is a structure that is used to implement VB's BYREF variants. + it is basically a tuple of an address of some data along with a TypeHandle + that indicates the type of the address */ +class TypedByRef +{ +public: + + PTR_VOID data; + TypeHandle type; +}; + +typedef DPTR(TypedByRef) PTR_TypedByRef; + +typedef Array I1Array; +typedef Array I2Array; +typedef Array I4Array; +typedef Array I8Array; +typedef Array R4Array; +typedef Array R8Array; +typedef Array U1Array; +typedef Array BOOLArray; +typedef Array U2Array; +typedef Array CHARArray; +typedef Array U4Array; +typedef Array U8Array; +typedef Array UPTRArray; +typedef PtrArray PTRArray; + +typedef DPTR(I1Array) PTR_I1Array; +typedef DPTR(I2Array) PTR_I2Array; +typedef DPTR(I4Array) PTR_I4Array; +typedef DPTR(I8Array) PTR_I8Array; +typedef DPTR(R4Array) PTR_R4Array; +typedef DPTR(R8Array) PTR_R8Array; +typedef DPTR(U1Array) PTR_U1Array; +typedef DPTR(BOOLArray) PTR_BOOLArray; +typedef DPTR(U2Array) PTR_U2Array; +typedef DPTR(CHARArray) PTR_CHARArray; +typedef DPTR(U4Array) PTR_U4Array; +typedef DPTR(U8Array) PTR_U8Array; +typedef DPTR(UPTRArray) PTR_UPTRArray; +typedef DPTR(PTRArray) PTR_PTRArray; + +class StringObject; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF BASEARRAYREF; +typedef REF I1ARRAYREF; +typedef REF I2ARRAYREF; +typedef REF I4ARRAYREF; +typedef REF I8ARRAYREF; +typedef REF R4ARRAYREF; +typedef REF R8ARRAYREF; +typedef REF U1ARRAYREF; +typedef REF BOOLARRAYREF; +typedef REF U2ARRAYREF; +typedef REF U4ARRAYREF; +typedef REF U8ARRAYREF; +typedef REF UPTRARRAYREF; +typedef REF CHARARRAYREF; +typedef REF PTRARRAYREF; // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays. +typedef REF STRINGREF; + +#else // USE_CHECKED_OBJECTREFS + +typedef PTR_ArrayBase BASEARRAYREF; +typedef PTR_I1Array I1ARRAYREF; +typedef PTR_I2Array I2ARRAYREF; +typedef PTR_I4Array I4ARRAYREF; +typedef PTR_I8Array I8ARRAYREF; +typedef PTR_R4Array R4ARRAYREF; +typedef PTR_R8Array R8ARRAYREF; +typedef PTR_U1Array U1ARRAYREF; +typedef PTR_BOOLArray BOOLARRAYREF; +typedef PTR_U2Array U2ARRAYREF; +typedef PTR_U4Array U4ARRAYREF; +typedef PTR_U8Array U8ARRAYREF; +typedef PTR_UPTRArray UPTRARRAYREF; +typedef PTR_CHARArray CHARARRAYREF; +typedef PTR_PTRArray PTRARRAYREF; // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays. +typedef PTR_StringObject STRINGREF; + +#endif // USE_CHECKED_OBJECTREFS + + +#include + + +/* + * StringObject + * + * Special String implementation for performance. + * + * m_StringLength - Length of string in number of WCHARs + * m_FirstChar - The string buffer + * + */ + + +class StringObject : public Object +{ +#ifdef DACCESS_COMPILE + friend class ClrDataAccess; +#endif + friend class GCHeap; + friend class JIT_TrialAlloc; + friend class CheckAsmOffsets; + friend class COMString; + + private: + DWORD m_StringLength; + WCHAR m_FirstChar; + + public: + VOID SetStringLength(DWORD len) { LIMITED_METHOD_CONTRACT; _ASSERTE(len >= 0); m_StringLength = len; } + + protected: + StringObject() {LIMITED_METHOD_CONTRACT; } + ~StringObject() {LIMITED_METHOD_CONTRACT; } + + public: + static DWORD GetBaseSize(); + static SIZE_T GetSize(DWORD stringLength); + + DWORD GetStringLength() { LIMITED_METHOD_DAC_CONTRACT; return( m_StringLength );} + WCHAR* GetBuffer() { LIMITED_METHOD_CONTRACT; return (WCHAR*)( dac_cast(this) + offsetof(StringObject, m_FirstChar) ); } + + static UINT GetBufferOffset() + { + LIMITED_METHOD_DAC_CONTRACT; + return (UINT)(offsetof(StringObject, m_FirstChar)); + } + static UINT GetStringLengthOffset() + { + LIMITED_METHOD_CONTRACT; + return (UINT)(offsetof(StringObject, m_StringLength)); + } + VOID GetSString(SString &result) + { + WRAPPER_NO_CONTRACT; + result.Set(GetBuffer(), GetStringLength()); + } + //======================================================================== + // Creates a System.String object. All the functions that take a length + // or a count of bytes will add the null terminator after length + // characters. So this means that if you have a string that has 5 + // characters and the null terminator you should pass in 5 and NOT 6. + //======================================================================== + static STRINGREF NewString(int length); + static STRINGREF NewString(const WCHAR *pwsz); + static STRINGREF NewString(const WCHAR *pwsz, int length); + static STRINGREF NewString(LPCUTF8 psz); + static STRINGREF NewString(LPCUTF8 psz, int cBytes); + + static STRINGREF GetEmptyString(); + static STRINGREF* GetEmptyStringRefPtr(void** pinnedString); + + static STRINGREF* InitEmptyStringRefPtr(); + + /*=================RefInterpretGetStringValuesDangerousForGC====================== + **N.B.: This performs no range checking and relies on the caller to have done this. + **Args: (IN)ref -- the String to be interpretted. + ** (OUT)chars -- a pointer to the characters in the buffer. + ** (OUT)length -- a pointer to the length of the buffer. + **Returns: void. + **Exceptions: None. + ==============================================================================*/ + // !!!! If you use this function, you have to be careful because chars is a pointer + // !!!! to the data buffer of ref. If GC happens after this call, you need to make + // !!!! sure that you have a pin handle on ref, or use GCPROTECT_BEGINPINNING on ref. + void RefInterpretGetStringValuesDangerousForGC(_Outptr_result_buffer_(*length + 1) WCHAR **chars, int *length) { + WRAPPER_NO_CONTRACT; + + _ASSERTE(GetGCSafeMethodTable() == g_pStringClass); + *length = GetStringLength(); + *chars = GetBuffer(); +#ifdef _DEBUG + EnableStressHeapHelper(); +#endif + } + + +private: + static STRINGREF* EmptyStringRefPtr; + static bool EmptyStringIsFrozen; + + friend struct ::cdac_data; +}; + +template<> +struct cdac_data +{ + static constexpr size_t m_FirstChar = offsetof(StringObject, m_FirstChar); + static constexpr size_t m_StringLength = offsetof(StringObject, m_StringLength); +}; + +/*================================GetEmptyString================================ +**Get a reference to the empty string. If we haven't already gotten one, we +**query the String class for a pointer to the empty string that we know was +**created at startup. +** +**Args: None +**Returns: A STRINGREF to the EmptyString +**Exceptions: None +==============================================================================*/ +inline STRINGREF StringObject::GetEmptyString() { + + CONTRACTL { + THROWS; + MODE_COOPERATIVE; + GC_TRIGGERS; + } CONTRACTL_END; + STRINGREF* refptr = EmptyStringRefPtr; + + //If we've never gotten a reference to the EmptyString, we need to go get one. + if (refptr==NULL) { + refptr = InitEmptyStringRefPtr(); + } + //We've already have a reference to the EmptyString, so we can just return it. + return *refptr; +} + +inline STRINGREF* StringObject::GetEmptyStringRefPtr(void** pinnedString) { + + CONTRACTL { + THROWS; + MODE_ANY; + GC_TRIGGERS; + } CONTRACTL_END; + + STRINGREF* refptr = EmptyStringRefPtr; + + //If we've never gotten a reference to the EmptyString, we need to go get one. + if (refptr == nullptr) + { + refptr = InitEmptyStringRefPtr(); + } + + if (EmptyStringIsFrozen && pinnedString != nullptr) + { + *pinnedString = *(void**)refptr; + } + + //We've already have a reference to the EmptyString, so we can just return it. + return refptr; +} + +// This is the Class version of the Reflection object. +// A Class has adddition information. +// For a ReflectClassBaseObject the m_pData is a pointer to a FieldDesc array that +// contains all of the final static primitives if its defined. +// m_cnt = the number of elements defined in the m_pData FieldDesc array. -1 means +// this hasn't yet been defined. +class ReflectClassBaseObject : public Object +{ + friend class CoreLibBinder; + +protected: + OBJECTREF m_keepalive; + OBJECTREF m_cache; + TypeHandle m_typeHandle; + +#ifdef _DEBUG + void TypeCheck() + { + CONTRACTL + { + NOTHROW; + MODE_COOPERATIVE; + GC_NOTRIGGER; + } + CONTRACTL_END; + + MethodTable *pMT = GetMethodTable(); + while (pMT != g_pRuntimeTypeClass && pMT != NULL) + { + pMT = pMT->GetParentMethodTable(); + } + _ASSERTE(pMT == g_pRuntimeTypeClass); + } +#endif // _DEBUG + +public: + void SetType(TypeHandle type) { + CONTRACTL + { + NOTHROW; + MODE_COOPERATIVE; + GC_NOTRIGGER; + } + CONTRACTL_END; + + INDEBUG(TypeCheck()); + m_typeHandle = type; + } + + void SetKeepAlive(OBJECTREF keepalive) + { + CONTRACTL + { + NOTHROW; + MODE_COOPERATIVE; + GC_NOTRIGGER; + } + CONTRACTL_END; + + INDEBUG(TypeCheck()); + SetObjectReference(&m_keepalive, keepalive); + } + + TypeHandle GetType() { + CONTRACTL + { + NOTHROW; + MODE_COOPERATIVE; + GC_NOTRIGGER; + } + CONTRACTL_END; + + INDEBUG(TypeCheck()); + return m_typeHandle; + } + +}; + +// This is the Method version of the Reflection object. +// A Method has additional information: +// m_pMD - A pointer to the actual MethodDesc of the method. +// m_object - a field that has a reference type in it. Used only for RuntimeMethodInfoStub to keep the real type alive. +// This structure matches the structure up to the m_pMD for several different managed types. +// (RuntimeConstructorInfo, RuntimeMethodInfo, and RuntimeMethodInfoStub). These types are unrelated in the type +// system except that they all implement a particular interface. It is important that such interface is not attached to any +// type that does not sufficiently match this data structure. +class ReflectMethodObject : public Object +{ + friend class CoreLibBinder; + +protected: + OBJECTREF m_object; + OBJECTREF m_empty1; + OBJECTREF m_empty2; + OBJECTREF m_empty3; + OBJECTREF m_empty4; + OBJECTREF m_empty5; + OBJECTREF m_empty6; + OBJECTREF m_empty7; + OBJECTREF m_empty8; + MethodDesc * m_pMD; + +public: + void SetMethod(MethodDesc *pMethod) { + LIMITED_METHOD_CONTRACT; + m_pMD = pMethod; + } + + // This must only be called on instances of ReflectMethodObject that are actually RuntimeMethodInfoStub + void SetKeepAlive(OBJECTREF keepalive) + { + WRAPPER_NO_CONTRACT; + SetObjectReference(&m_object, keepalive); + } + + MethodDesc *GetMethod() { + LIMITED_METHOD_CONTRACT; + return m_pMD; + } + +}; + +// This is the Field version of the Reflection object. +// A Method has additional information: +// m_pFD - A pointer to the actual MethodDesc of the method. +// m_object - a field that has a reference type in it. Used only for RuntimeFieldInfoStub to keep the real type alive. +// This structure matches the structure up to the m_pFD for several different managed types. +// (RtFieldInfo and RuntimeFieldInfoStub). These types are unrelated in the type +// system except that they all implement a particular interface. It is important that such interface is not attached to any +// type that does not sufficiently match this data structure. +class ReflectFieldObject : public Object +{ + friend class CoreLibBinder; + +protected: + OBJECTREF m_object; + OBJECTREF m_empty1; + INT32 m_empty2; + OBJECTREF m_empty3; + OBJECTREF m_empty4; + OBJECTREF m_empty5; + FieldDesc * m_pFD; + +public: + void SetField(FieldDesc *pField) { + LIMITED_METHOD_CONTRACT; + m_pFD = pField; + } + + // This must only be called on instances of ReflectFieldObject that are actually RuntimeFieldInfoStub + void SetKeepAlive(OBJECTREF keepalive) + { + WRAPPER_NO_CONTRACT; + SetObjectReference(&m_object, keepalive); + } + + FieldDesc *GetField() { + LIMITED_METHOD_CONTRACT; + return m_pFD; + } +}; + +// ReflectModuleBaseObject +// This class is the base class for managed Module. +// This class will connect the Object back to the underlying VM representation +// m_ReflectClass -- This is the real Class that was used for reflection +// This class was used to get at this object +// m_pData -- this is a generic pointer which usually points CorModule +// +class ReflectModuleBaseObject : public Object +{ + friend class CoreLibBinder; + + protected: + // READ ME: + // Modifying the order or fields of this object may require other changes to the + // classlib class definition of this object. + OBJECTREF m_runtimeType; + OBJECTREF m_runtimeAssembly; + Module* m_pData; // Pointer to the Module + + protected: + ReflectModuleBaseObject() {LIMITED_METHOD_CONTRACT;} + ~ReflectModuleBaseObject() {LIMITED_METHOD_CONTRACT;} + + public: + void SetModule(Module* p) { + LIMITED_METHOD_CONTRACT; + m_pData = p; + } + Module* GetModule() { + LIMITED_METHOD_CONTRACT; + return m_pData; + } + void SetAssembly(OBJECTREF assembly) + { + WRAPPER_NO_CONTRACT; + SetObjectReference(&m_runtimeAssembly, assembly); + } +}; + +class ThreadBaseObject; +class ExecutionContextObject: public Object +{ + friend class CoreLibBinder; + +private: + // These field are also defined in the managed representation. (SecurityContext.cs)If you + // add or change these field you must also change the managed code so that + // it matches these. This is necessary so that the object is the proper + // size. + OBJECTREF m_localValues; + OBJECTREF m_localChangeNotifications; + CLR_BOOL m_isFlowSuppressed; + +public: + bool IsFlowSuppressed() const + { + LIMITED_METHOD_CONTRACT; + return m_isFlowSuppressed; + } +}; + +typedef DPTR(class CultureInfoBaseObject) PTR_CultureInfoBaseObject; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF EXECUTIONCONTEXTREF; +typedef REF ARRAYBASEREF; + +#else +typedef ExecutionContextObject* EXECUTIONCONTEXTREF; +typedef PTR_ArrayBase ARRAYBASEREF; +#endif + +typedef DPTR(class ThreadBaseObject) PTR_ThreadBaseObject; +class ThreadBaseObject : public Object +{ + friend class ClrDataAccess; + friend class CoreLibBinder; + friend class Object; + +private: + + // These field are also defined in the managed representation. If you + // add or change these field you must also change the managed code so that + // it matches these. This is necessary so that the object is the proper + // size. The order here must match that order which the loader will choose + // when laying out the managed class. Note that the layouts are checked + // at run time, not compile time. + OBJECTREF m_ExecutionContext; + OBJECTREF m_SynchronizationContext; + STRINGREF m_Name; + OBJECTREF m_StartHelper; +#ifdef TARGET_UNIX + OBJECTREF m_WaitInfo; + OBJECTREF m_joinEvent; +#endif // TARGET_UNIX + + // The next field (m_InternalThread) is declared as IntPtr in the managed + // definition of Thread. The loader will sort it next. + + // m_InternalThread is always valid -- unless the thread has finalized and been + // resurrected. (The thread stopped running before it was finalized). + Thread *m_InternalThread; + INT32 m_Priority; + + // We need to cache the thread id in managed code for perf reasons. + INT32 m_ManagedThreadId; + + // Only used by managed code, see comment there + bool m_MayNeedResetForThreadPool; + + // Set in unmanaged code and read in managed code. + bool m_IsDead; + + bool m_IsThreadPool; + +protected: + // the ctor and dtor can do no useful work. + ThreadBaseObject() {LIMITED_METHOD_CONTRACT;}; + ~ThreadBaseObject() {LIMITED_METHOD_CONTRACT;}; + +public: + Thread *GetInternal() + { + LIMITED_METHOD_CONTRACT; + return m_InternalThread; + } + + void SetInternal(Thread *it); + void ClearInternal(); + + void SetManagedThreadId(INT32 id) + { + LIMITED_METHOD_CONTRACT; + m_ManagedThreadId = id; + } + + STRINGREF GetName() { + LIMITED_METHOD_CONTRACT; + return m_Name; + } + + OBJECTREF GetExecutionContext() + { + LIMITED_METHOD_CONTRACT; + return m_ExecutionContext; + } + + void InitExisting(); + + void ResetStartHelper() + { + LIMITED_METHOD_CONTRACT + m_StartHelper = NULL; + } + + void SetPriority(INT32 priority) + { + LIMITED_METHOD_CONTRACT; + m_Priority = priority; + } + + INT32 GetPriority() const + { + LIMITED_METHOD_CONTRACT; + return m_Priority; + } + + void SetIsDead() + { + LIMITED_METHOD_CONTRACT; + m_IsDead = true; + } +}; + +// AssemblyBaseObject +// This class is the base class for assemblies +// +class AssemblyBaseObject : public Object +{ + friend class Assembly; + friend class CoreLibBinder; + + protected: + // READ ME: + // Modifying the order or fields of this object may require other changes to the + // classlib class definition of this object. + OBJECTREF m_pModuleEventHandler; // Delegate for 'resolve module' event + STRINGREF m_fullname; // Slot for storing assemblies fullname + OBJECTREF m_pSyncRoot; // Pointer to loader allocator to keep collectible types alive, and to serve as the syncroot for assembly building in ref.emit + Assembly* m_pAssembly; // Pointer to the Assembly Structure + + protected: + AssemblyBaseObject() { LIMITED_METHOD_CONTRACT; } + ~AssemblyBaseObject() { LIMITED_METHOD_CONTRACT; } + + public: + + void SetAssembly(Assembly* p) + { + LIMITED_METHOD_CONTRACT; + m_pAssembly = p; + } + + Assembly* GetAssembly(); + + void SetSyncRoot(OBJECTREF pSyncRoot) + { + WRAPPER_NO_CONTRACT; + SetObjectReference(&m_pSyncRoot, pSyncRoot); + } +}; + +// AssemblyLoadContextBaseObject +// This class is the base class for AssemblyLoadContext +// +#if defined(TARGET_X86) && !defined(TARGET_UNIX) +#include "pshpack4.h" +#endif // defined(TARGET_X86) && !defined(TARGET_UNIX) +class AssemblyLoadContextBaseObject : public Object +{ + friend class CoreLibBinder; + + protected: + // READ ME: + // Modifying the order or fields of this object may require other changes to the + // classlib class definition of this object. +#ifdef TARGET_64BIT + OBJECTREF _unloadLock; + OBJECTREF _resolvingUnmanagedDll; + OBJECTREF _resolving; + OBJECTREF _unloading; + OBJECTREF _name; + INT_PTR _nativeAssemblyLoadContext; + int64_t _id; // On 64-bit platforms this is a value type so it is placed after references and pointers + DWORD _state; + CLR_BOOL _isCollectible; +#else // TARGET_64BIT + int64_t _id; // On 32-bit platforms this 64-bit value type is larger than a pointer so JIT places it first + OBJECTREF _unloadLock; + OBJECTREF _resolvingUnmanagedDll; + OBJECTREF _resolving; + OBJECTREF _unloading; + OBJECTREF _name; + INT_PTR _nativeAssemblyLoadContext; + DWORD _state; + CLR_BOOL _isCollectible; +#endif // TARGET_64BIT + + protected: + AssemblyLoadContextBaseObject() { LIMITED_METHOD_CONTRACT; } + ~AssemblyLoadContextBaseObject() { LIMITED_METHOD_CONTRACT; } + + public: + INT_PTR GetNativeAssemblyBinder() { LIMITED_METHOD_CONTRACT; return _nativeAssemblyLoadContext; } +}; +#if defined(TARGET_X86) && !defined(TARGET_UNIX) +#include "poppack.h" +#endif // defined(TARGET_X86) && !defined(TARGET_UNIX) + +struct NativeAssemblyNameParts +{ + PCWSTR _pName; + UINT16 _major, _minor, _build, _revision; + PCWSTR _pCultureName; + BYTE* _pPublicKeyOrToken; + int _cbPublicKeyOrToken; + DWORD _flags; +}; + +// AssemblyNameBaseObject +// This class is the base class for assembly names +// +class AssemblyNameBaseObject : public Object +{ + // Dummy definition +}; + +class WeakReferenceObject : public Object +{ +public: + uintptr_t m_taggedHandle; +}; + + +typedef DPTR(class ContinuationObject) PTR_ContinuationObject; +class ContinuationObject; + +#ifdef USE_CHECKED_OBJECTREFS + +typedef REF REFLECTMODULEBASEREF; + +typedef REF REFLECTCLASSBASEREF; + +typedef REF REFLECTMETHODREF; + +typedef REF REFLECTFIELDREF; + +typedef REF THREADBASEREF; + +typedef REF ASSEMBLYREF; + +typedef REF ASSEMBLYLOADCONTEXTREF; + +typedef REF ASSEMBLYNAMEREF; + +typedef REF THREADBASEREF; + +typedef REF CONTINUATIONREF; + +inline ARG_SLOT ObjToArgSlot(OBJECTREF objRef) +{ + LIMITED_METHOD_CONTRACT; + LPVOID v; + v = OBJECTREFToObject(objRef); + return (ARG_SLOT)(SIZE_T)v; +} + +inline OBJECTREF ArgSlotToObj(ARG_SLOT i) +{ + LIMITED_METHOD_CONTRACT; + LPVOID v; + v = (LPVOID)(SIZE_T)i; + return ObjectToOBJECTREF ((Object*)v); +} + +inline ARG_SLOT StringToArgSlot(STRINGREF sr) +{ + LIMITED_METHOD_CONTRACT; + LPVOID v; + v = OBJECTREFToObject(sr); + return (ARG_SLOT)(SIZE_T)v; +} + +inline STRINGREF ArgSlotToString(ARG_SLOT s) +{ + LIMITED_METHOD_CONTRACT; + LPVOID v; + v = (LPVOID)(SIZE_T)s; + return ObjectToSTRINGREF ((StringObject*)v); +} + +#else // USE_CHECKED_OBJECTREFS + +typedef PTR_ReflectModuleBaseObject REFLECTMODULEBASEREF; +typedef PTR_ReflectClassBaseObject REFLECTCLASSBASEREF; +typedef PTR_ReflectMethodObject REFLECTMETHODREF; +typedef PTR_ReflectFieldObject REFLECTFIELDREF; +typedef PTR_ThreadBaseObject THREADBASEREF; +typedef PTR_AssemblyBaseObject ASSEMBLYREF; +typedef PTR_AssemblyLoadContextBaseObject ASSEMBLYLOADCONTEXTREF; +typedef PTR_AssemblyNameBaseObject ASSEMBLYNAMEREF; +typedef PTR_ThreadBaseObject THREADBASEREF; +typedef PTR_ContinuationObject CONTINUATIONREF; + +#define ObjToArgSlot(objref) ((ARG_SLOT)(SIZE_T)(objref)) +#define ArgSlotToObj(s) ((OBJECTREF)(SIZE_T)(s)) + +#define StringToArgSlot(objref) ((ARG_SLOT)(SIZE_T)(objref)) +#define ArgSlotToString(s) ((STRINGREF)(SIZE_T)(s)) + +#endif //USE_CHECKED_OBJECTREFS + +#define PtrToArgSlot(ptr) ((ARG_SLOT)(SIZE_T)(ptr)) +#define ArgSlotToPtr(s) ((LPVOID)(SIZE_T)(s)) + +#define BoolToArgSlot(b) ((ARG_SLOT)(CLR_BOOL)(!!(b))) +#define ArgSlotToBool(s) ((BOOL)(s)) + +STRINGREF AllocateString(SString sstr); + +#ifdef FEATURE_COMINTEROP + +//------------------------------------------------------------- +// class ComObject, Exposed class __ComObject +// +// +//------------------------------------------------------------- +class ComObject : public Object +{ + friend class CoreLibBinder; + +protected: + + ComObject() + {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly + ~ComObject(){LIMITED_METHOD_CONTRACT;}; + +public: + OBJECTREF m_ObjectToDataMap; + + //-------------------------------------------------------------------- + // SupportsInterface + static BOOL SupportsInterface(OBJECTREF oref, MethodTable* pIntfTable); + + //-------------------------------------------------------------------- + // SupportsInterface + static void ThrowInvalidCastException(OBJECTREF *pObj, MethodTable* pCastToMT); + + //----------------------------------------------------------------- + // GetComIPFromRCW + static IUnknown* GetComIPFromRCW(OBJECTREF *pObj, MethodTable* pIntfTable); + + //----------------------------------------------------------------- + // GetComIPFromRCWThrowing + static IUnknown* GetComIPFromRCWThrowing(OBJECTREF *pObj, MethodTable* pIntfTable); + + //----------------------------------------------------------- + // create an empty ComObjectRef + static OBJECTREF CreateComObjectRef(MethodTable* pMT); + + //----------------------------------------------------------- + // Release all the data associated with the __ComObject. + static void ReleaseAllData(OBJECTREF oref); +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF COMOBJECTREF; +#else +typedef ComObject* COMOBJECTREF; +#endif + + +//------------------------------------------------------------- +// class UnknownWrapper, Exposed class UnknownWrapper +// +// +//------------------------------------------------------------- +class UnknownWrapper : public Object +{ +protected: + + UnknownWrapper(UnknownWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. + UnknownWrapper() {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly + ~UnknownWrapper() {LIMITED_METHOD_CONTRACT;}; + + OBJECTREF m_WrappedObject; + +public: + OBJECTREF GetWrappedObject() + { + LIMITED_METHOD_CONTRACT; + return m_WrappedObject; + } + + void SetWrappedObject(OBJECTREF pWrappedObject) + { + LIMITED_METHOD_CONTRACT; + m_WrappedObject = pWrappedObject; + } +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF UNKNOWNWRAPPEROBJECTREF; +#else +typedef UnknownWrapper* UNKNOWNWRAPPEROBJECTREF; +#endif + + +//------------------------------------------------------------- +// class DispatchWrapper, Exposed class DispatchWrapper +// +// +//------------------------------------------------------------- +class DispatchWrapper : public Object +{ +protected: + + DispatchWrapper(DispatchWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. + DispatchWrapper() {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly + ~DispatchWrapper() {LIMITED_METHOD_CONTRACT;}; + + OBJECTREF m_WrappedObject; + +public: + OBJECTREF GetWrappedObject() + { + LIMITED_METHOD_CONTRACT; + return m_WrappedObject; + } + + void SetWrappedObject(OBJECTREF pWrappedObject) + { + LIMITED_METHOD_CONTRACT; + m_WrappedObject = pWrappedObject; + } +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF DISPATCHWRAPPEROBJECTREF; +#else +typedef DispatchWrapper* DISPATCHWRAPPEROBJECTREF; +#endif + + +//------------------------------------------------------------- +// class VariantWrapper, Exposed class VARIANTWRAPPEROBJECTREF +// +// +//------------------------------------------------------------- +class VariantWrapper : public Object +{ +protected: + + VariantWrapper(VariantWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. + VariantWrapper() {LIMITED_METHOD_CONTRACT}; // don't instantiate this class directly + ~VariantWrapper() {LIMITED_METHOD_CONTRACT}; + + OBJECTREF m_WrappedObject; + +public: + OBJECTREF GetWrappedObject() + { + LIMITED_METHOD_CONTRACT; + return m_WrappedObject; + } + + void SetWrappedObject(OBJECTREF pWrappedObject) + { + LIMITED_METHOD_CONTRACT; + m_WrappedObject = pWrappedObject; + } +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF VARIANTWRAPPEROBJECTREF; +#else +typedef VariantWrapper* VARIANTWRAPPEROBJECTREF; +#endif + + +//------------------------------------------------------------- +// class ErrorWrapper, Exposed class ErrorWrapper +// +// +//------------------------------------------------------------- +class ErrorWrapper : public Object +{ +protected: + + ErrorWrapper(ErrorWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. + ErrorWrapper() {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly + ~ErrorWrapper() {LIMITED_METHOD_CONTRACT;}; + + INT32 m_ErrorCode; + +public: + INT32 GetErrorCode() + { + LIMITED_METHOD_CONTRACT; + return m_ErrorCode; + } + + void SetErrorCode(int ErrorCode) + { + LIMITED_METHOD_CONTRACT; + m_ErrorCode = ErrorCode; + } +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF ERRORWRAPPEROBJECTREF; +#else +typedef ErrorWrapper* ERRORWRAPPEROBJECTREF; +#endif + + +//------------------------------------------------------------- +// class CurrencyWrapper, Exposed class CurrencyWrapper +// +// +//------------------------------------------------------------- + +// Keep this in sync with code:MethodTableBuilder.CheckForSystemTypes where +// alignment requirement of the managed System.Decimal structure is computed. +#if !defined(ALIGN_ACCESS) && !defined(FEATURE_64BIT_ALIGNMENT) +#include +#endif // !ALIGN_ACCESS && !FEATURE_64BIT_ALIGNMENT + +class CurrencyWrapper : public Object +{ +protected: + + CurrencyWrapper(CurrencyWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. + CurrencyWrapper() {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly + ~CurrencyWrapper() {LIMITED_METHOD_CONTRACT;}; + + DECIMAL m_WrappedObject; + +public: + DECIMAL GetWrappedObject() + { + LIMITED_METHOD_CONTRACT; + return m_WrappedObject; + } + + void SetWrappedObject(DECIMAL WrappedObj) + { + LIMITED_METHOD_CONTRACT; + m_WrappedObject = WrappedObj; + } +}; + +#if !defined(ALIGN_ACCESS) && !defined(FEATURE_64BIT_ALIGNMENT) +#include +#endif // !ALIGN_ACCESS && !FEATURE_64BIT_ALIGNMENT + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF CURRENCYWRAPPEROBJECTREF; +#else +typedef CurrencyWrapper* CURRENCYWRAPPEROBJECTREF; +#endif + +//------------------------------------------------------------- +// class BStrWrapper, Exposed class BSTRWRAPPEROBJECTREF +// +// +//------------------------------------------------------------- +class BStrWrapper : public Object +{ +protected: + + BStrWrapper(BStrWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. + BStrWrapper() {LIMITED_METHOD_CONTRACT}; // don't instantiate this class directly + ~BStrWrapper() {LIMITED_METHOD_CONTRACT}; + + STRINGREF m_WrappedObject; + +public: + STRINGREF GetWrappedObject() + { + LIMITED_METHOD_CONTRACT; + return m_WrappedObject; + } + + void SetWrappedObject(STRINGREF pWrappedObject) + { + LIMITED_METHOD_CONTRACT; + m_WrappedObject = pWrappedObject; + } +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF BSTRWRAPPEROBJECTREF; +#else +typedef BStrWrapper* BSTRWRAPPEROBJECTREF; +#endif + +#endif // FEATURE_COMINTEROP + + +// This class corresponds to System.MulticastDelegate on the managed side. +class DelegateObject : public Object +{ + friend class CheckAsmOffsets; + friend class CoreLibBinder; + friend struct ::cdac_data; + +public: + BOOL IsWrapperDelegate() { LIMITED_METHOD_CONTRACT; return _methodPtrAux == 0; } + + OBJECTREF GetTarget() { LIMITED_METHOD_CONTRACT; return _target; } + void SetTarget(OBJECTREF target) { WRAPPER_NO_CONTRACT; SetObjectReference(&_target, target); } + static int GetOffsetOfTarget() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _target); } + + MethodDesc* GetMethodDesc() { LIMITED_METHOD_CONTRACT; return _methodDesc; } + void SetMethodDesc(MethodDesc* methodDesc) { LIMITED_METHOD_CONTRACT; _methodDesc = methodDesc; } + static int GetOffsetOfMethodDesc() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodDesc); } + + PCODE GetMethodPtr() { LIMITED_METHOD_CONTRACT; return _methodPtr; } + void SetMethodPtr(PCODE methodPtr) { LIMITED_METHOD_CONTRACT; _methodPtr = methodPtr; } + static int GetOffsetOfMethodPtr() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtr); } + + PCODE GetMethodPtrAux() { LIMITED_METHOD_CONTRACT; return _methodPtrAux; } + void SetMethodPtrAux(PCODE methodPtrAux) { LIMITED_METHOD_CONTRACT; _methodPtrAux = methodPtrAux; } + static int GetOffsetOfMethodPtrAux() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtrAux); } + + OBJECTREF GetInvocationList() { LIMITED_METHOD_CONTRACT; return _invocationList; } + void SetInvocationList(OBJECTREF invocationList) { WRAPPER_NO_CONTRACT; SetObjectReference(&_invocationList, invocationList); } + static int GetOffsetOfInvocationList() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _invocationList); } + + INT_PTR GetInvocationCount() { LIMITED_METHOD_CONTRACT; return _invocationCount; } + void SetInvocationCount(INT_PTR invocationCount) { LIMITED_METHOD_CONTRACT; _invocationCount = invocationCount; } + static int GetOffsetOfInvocationCount() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _invocationCount); } + + // README: + // If you modify the order of these fields, make sure to update the definition in + // BCL for this object. +private: + // System.Delegate +<<<<<<< HEAD + OBJECTREF _target; + MethodDesc* _methodDesc; +======= + OBJECTREF _methodBase; + OBJECTREF _target; +>>>>>>> upstream/main + PCODE _methodPtr; + PCODE _methodPtrAux; + // System.MulticastDelegate + OBJECTREF _invocationList; + INT_PTR _invocationCount; +}; + +<<<<<<< HEAD +#define OFFSETOF__DelegateObject__target OBJECT_SIZE /* m_pMethTab */ +#define OFFSETOF__DelegateObject__methodDesc (OFFSETOF__DelegateObject__target + TARGET_POINTER_SIZE /* _target */) +#define OFFSETOF__DelegateObject__methodPtr (OFFSETOF__DelegateObject__methodDesc + TARGET_POINTER_SIZE /* _methodDesc */) +======= +#define OFFSETOF__DelegateObject__target (OBJECT_SIZE /* m_pMethTab */ + TARGET_POINTER_SIZE /* _methodBase */) +#define OFFSETOF__DelegateObject__methodPtr (OFFSETOF__DelegateObject__target + TARGET_POINTER_SIZE /* _target */) +>>>>>>> upstream/main +#define OFFSETOF__DelegateObject__methodPtrAux (OFFSETOF__DelegateObject__methodPtr + TARGET_POINTER_SIZE /* _methodPtr */) + +template<> +struct cdac_data +{ + static constexpr size_t Target = offsetof(DelegateObject, _target); + static constexpr size_t MethodPtr = offsetof(DelegateObject, _methodPtr); + static constexpr size_t MethodPtrAux = offsetof(DelegateObject, _methodPtrAux); + static constexpr size_t InvocationCount = offsetof(DelegateObject, _invocationCount); +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF DELEGATEREF; +#else // USE_CHECKED_OBJECTREFS +typedef DelegateObject* DELEGATEREF; +#endif // USE_CHECKED_OBJECTREFS + + +struct StackTraceElement; +class ClrDataAccess; + + +typedef DPTR(StackTraceElement) PTR_StackTraceElement; + +class StackTraceArray +{ + struct ArrayHeader + { + uint32_t m_size; + uint32_t m_keepAliveItemsCount; + Thread * m_thread; + }; + + typedef DPTR(ArrayHeader) PTR_ArrayHeader; + +public: + StackTraceArray() + : m_array(static_cast(NULL)) + { + WRAPPER_NO_CONTRACT; + } + + StackTraceArray(I1ARRAYREF array) + : m_array(array) + { + LIMITED_METHOD_CONTRACT; + } + + void Set(I1ARRAYREF array) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + SUPPORTS_DAC; + m_array = array; + } + + uint32_t Size() const + { + WRAPPER_NO_CONTRACT; + if (!m_array) + return 0; + else + return GetSize(); + } + + StackTraceElement const & operator[](size_t index) const; + StackTraceElement & operator[](size_t index); + + size_t Capacity() const; + void Append(StackTraceElement const * element); + void Allocate(size_t size); + + I1ARRAYREF Get() const + { + LIMITED_METHOD_DAC_CONTRACT; + return m_array; + } + + uint32_t CopyDataFrom(StackTraceArray const & src); + + Thread * GetObjectThread() const + { + WRAPPER_NO_CONTRACT; + return GetHeader()->m_thread; + } + + void SetSize(uint32_t size) + { + WRAPPER_NO_CONTRACT; + VolatileStore(&GetHeader()->m_size, size); + } + + void SetKeepAliveItemsCount(uint32_t count) + { + WRAPPER_NO_CONTRACT; + VolatileStore(&GetHeader()->m_keepAliveItemsCount, count); + } + + uint32_t GetKeepAliveItemsCount() const + { + WRAPPER_NO_CONTRACT; + return VolatileLoad(&GetHeader()->m_keepAliveItemsCount); + } + + // Compute the number of methods in the stack trace that can be collected. We need to store keepAlive + // objects (Resolver / LoaderAllocator) for these methods. + uint32_t ComputeKeepAliveItemsCount(); + + void MarkAsFrozen() + { + if (m_array != NULL) + { + GetHeader()->m_thread = (Thread *)(size_t)1; + } + } + + bool IsFrozen() const + { + return GetHeader()->m_thread == (Thread *)(size_t)1; + } + +private: + StackTraceArray(StackTraceArray const & rhs) = delete; + + StackTraceArray & operator=(StackTraceArray const & rhs) = delete; + + void CheckState() const; + + uint32_t GetSize() const + { + WRAPPER_NO_CONTRACT; + return VolatileLoad(&GetHeader()->m_size); + } + + void SetObjectThread() + { + WRAPPER_NO_CONTRACT; + GetHeader()->m_thread = GetThread(); + } + + StackTraceElement const * GetData() const + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return dac_cast(GetRaw() + sizeof(ArrayHeader)); + } + + PTR_StackTraceElement GetData() + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return dac_cast(GetRaw() + sizeof(ArrayHeader)); + } + + CLR_I1 const * GetRaw() const + { + WRAPPER_NO_CONTRACT; + _ASSERTE(!!m_array); + + return m_array->GetDirectPointerToNonObjectElements(); + } + + PTR_INT8 GetRaw() + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + _ASSERTE(!!m_array); + + return dac_cast(m_array->GetDirectPointerToNonObjectElements()); + } + + ArrayHeader const * GetHeader() const + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return dac_cast(GetRaw()); + } + + PTR_ArrayHeader GetHeader() + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return dac_cast(GetRaw()); + } + + void SetArray(I1ARRAYREF const & arr) + { + LIMITED_METHOD_CONTRACT; + m_array = arr; + } + +private: + // put only things here that can be protected with GCPROTECT + I1ARRAYREF m_array; + + friend struct ::cdac_data; +}; + +template<> +struct cdac_data +{ + static constexpr size_t Size = offsetof(StackTraceArray::ArrayHeader, m_size); + static constexpr size_t HeaderSize = sizeof(StackTraceArray::ArrayHeader); +}; + +#ifdef FEATURE_COLLECTIBLE_TYPES + +class LoaderAllocatorScoutObject : public Object +{ + friend class CoreLibBinder; + friend class LoaderAllocatorObject; + +protected: + LoaderAllocator * m_nativeLoaderAllocator; +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF LOADERALLOCATORSCOUTREF; +#else // USE_CHECKED_OBJECTREFS +typedef LoaderAllocatorScoutObject* LOADERALLOCATORSCOUTREF; +#endif // USE_CHECKED_OBJECTREFS + +class LoaderAllocatorObject : public Object +{ + friend class CoreLibBinder; + +public: + // All uses of this api must be safe lock-free reads used only for reading from the handle table + // The normal GetHandleTable can only be called while holding the handle table lock, but + // this is for use in lock-free scenarios + PTRARRAYREF DangerousGetHandleTable() + { + LIMITED_METHOD_DAC_CONTRACT; + return (PTRARRAYREF)ObjectToOBJECTREF(VolatileLoadWithoutBarrier((Object**)&m_pSlots)); + } + +#ifndef DACCESS_COMPILE + PTRARRAYREF GetHandleTable(); + void SetHandleTable(PTRARRAYREF handleTable); + INT32 GetSlotsUsed(); + void SetSlotsUsed(INT32 newSlotsUsed); +#endif // DACCESS_COMPILE + + void SetNativeLoaderAllocator(LoaderAllocator * pLoaderAllocator) + { + LIMITED_METHOD_CONTRACT; + m_pLoaderAllocatorScout->m_nativeLoaderAllocator = pLoaderAllocator; + } + + // README: + // If you modify the order of these fields, make sure to update the definition in + // BCL for this object. +protected: + LOADERALLOCATORSCOUTREF m_pLoaderAllocatorScout; + OBJECTREF m_pSlots; + INT32 m_slotsUsed; + OBJECTREF m_methodInstantiationsTable; +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF LOADERALLOCATORREF; +#else // USE_CHECKED_OBJECTREFS +typedef DPTR(LoaderAllocatorObject) PTR_LoaderAllocatorObject; +typedef PTR_LoaderAllocatorObject LOADERALLOCATORREF; +#endif // USE_CHECKED_OBJECTREFS + +#endif // FEATURE_COLLECTIBLE_TYPES + +typedef DPTR(class GenericCacheStruct) PTR_GenericCacheStruct; +class GenericCacheStruct +{ + friend class CoreLibBinder; + public: + + ARRAYBASEREF GetTable() const + { + LIMITED_METHOD_CONTRACT; + return _table; + } + + int32_t CacheElementCount() const + { + LIMITED_METHOD_CONTRACT; + return GetTable()->GetNumComponents() - 1; + } + + ARRAYBASEREF GetSentinelTable() const + { + LIMITED_METHOD_CONTRACT; + return _sentinelTable; + } + + void SetTable(ARRAYBASEREF table) + { + WRAPPER_NO_CONTRACT; + SetObjectReference((OBJECTREF*)&_table, (OBJECTREF)table); + } + + void SetLastFlushSize(int32_t lastFlushSize) + { + LIMITED_METHOD_CONTRACT; + _lastFlushSize = lastFlushSize; + } + + int32_t GetInitialCacheSize() const + { + LIMITED_METHOD_CONTRACT; + return _initialCacheSize; + } + +#ifdef DEBUG + static void ValidateLayout(MethodTable* pMTOfInstantiation); +#endif + + private: + // README: + // If you modify the order of these fields, make sure to update the definition in + // BCL for this object. + + ARRAYBASEREF _table; + ARRAYBASEREF _sentinelTable; + int32_t _lastFlushSize; + int32_t _initialCacheSize; + int32_t _maxCacheSize; +}; + +class ContinuationObject : public Object +{ + friend class CoreLibBinder; + + public: + CorInfoContinuationFlags GetFlags() const + { + LIMITED_METHOD_CONTRACT; + return (CorInfoContinuationFlags)Flags; + } + + void SetFlags(CorInfoContinuationFlags flags) + { + LIMITED_METHOD_CONTRACT; + Flags = (int32_t)flags; + } + + void SetResumeInfo(void* resumeInfo) + { + LIMITED_METHOD_CONTRACT; + ResumeInfo = resumeInfo; + } + + void* GetResumeInfo() const + { + LIMITED_METHOD_CONTRACT; + return ResumeInfo; + } + + void SetState(int32_t state) + { + LIMITED_METHOD_CONTRACT; + State = state; + } + + int32_t GetState() const + { + LIMITED_METHOD_CONTRACT; + return State; + } + + PTR_BYTE GetResultStorage() + { + LIMITED_METHOD_CONTRACT; + + uint32_t mask = (1u << CORINFO_CONTINUATION_RESULT_INDEX_NUM_BITS) - 1; + uint32_t index = ((uint32_t)Flags >> CORINFO_CONTINUATION_RESULT_INDEX_FIRST_BIT) & mask; + _ASSERTE(index != 0); + + uint32_t offset = OFFSETOF__CORINFO_Continuation__data + (index - 1) * TARGET_POINTER_SIZE; + PTR_BYTE dataAddress = dac_cast((dac_cast(this) + offset)); + return dataAddress; + } + + PTR_OBJECTREF GetExceptionObjectStorageOrNull() + { + LIMITED_METHOD_CONTRACT; + + uint32_t mask = (1u << CORINFO_CONTINUATION_EXCEPTION_INDEX_NUM_BITS) - 1; + uint32_t index = ((uint32_t)Flags >> CORINFO_CONTINUATION_EXCEPTION_INDEX_FIRST_BIT) & mask; + if (index == 0) + return NULL; + + uint32_t offset = OFFSETOF__CORINFO_Continuation__data + (index - 1) * TARGET_POINTER_SIZE; + PTR_BYTE dataAddress = dac_cast(dac_cast(this) + offset); + return dac_cast(dataAddress); + } + + PTR_OBJECTREF GetExecutionContextObjectStorageOrNull() + { + LIMITED_METHOD_CONTRACT; + + uint32_t mask = (1u << CORINFO_CONTINUATION_EXECUTION_CONTEXT_INDEX_NUM_BITS) - 1; + uint32_t index = ((uint32_t)Flags >> CORINFO_CONTINUATION_EXECUTION_CONTEXT_INDEX_FIRST_BIT) & mask; + if (index == 0) + return NULL; + + uint32_t offset = OFFSETOF__CORINFO_Continuation__data + (index - 1) * TARGET_POINTER_SIZE; + PTR_BYTE address = dac_cast(dac_cast(this) + offset); + return dac_cast(address); + } + + PTR_OBJECTREF GetContinuationContextObjectStorageOrNull() + { + LIMITED_METHOD_CONTRACT; + + uint32_t mask = (1u << CORINFO_CONTINUATION_CONTEXT_INDEX_NUM_BITS) - 1; + uint32_t index = ((uint32_t)Flags >> CORINFO_CONTINUATION_CONTEXT_INDEX_FIRST_BIT) & mask; + if (index == 0) + return NULL; + + uint32_t offset = OFFSETOF__CORINFO_Continuation__data + (index - 1) * TARGET_POINTER_SIZE; + PTR_BYTE address = dac_cast(dac_cast(this) + offset); + return dac_cast(address); + } + +#ifndef DACCESS_COMPILE + int32_t* GetFlagsAddress() + { + LIMITED_METHOD_CONTRACT; + return (int32_t*)&Flags; + } +#endif // DACCESS_COMPILE + +private: + // README: + // If you modify the order of these fields, make sure to update the definition in + // BCL for this object. + + CONTINUATIONREF Next; + void* ResumeInfo; + int32_t Flags; + int32_t State; +}; + +// This class corresponds to Exception on the managed side. +typedef DPTR(class ExceptionObject) PTR_ExceptionObject; +#include "pshpack4.h" +class ExceptionObject : public Object +{ + friend class CoreLibBinder; + +public: + void SetHResult(HRESULT hr) + { + LIMITED_METHOD_CONTRACT; + _HResult = hr; + } + + HRESULT GetHResult() + { + LIMITED_METHOD_CONTRACT; + return _HResult; + } + + void SetXCode(DWORD code) + { + LIMITED_METHOD_CONTRACT; + _xcode = code; + } + + DWORD GetXCode() + { + LIMITED_METHOD_CONTRACT; + return _xcode; + } + + void SetXPtrs(void* xptrs) + { + LIMITED_METHOD_CONTRACT; + _xptrs = xptrs; + } + + void* GetXPtrs() + { + LIMITED_METHOD_CONTRACT; + return _xptrs; + } + + void SetStackTrace(OBJECTREF stackTrace); + + void GetStackTrace(StackTraceArray & stackTrace, PTRARRAYREF * outKeepaliveArray = NULL) const + { +#ifdef DACCESS_COMPILE + return GetStackTrace(stackTrace, outKeepaliveArray, NULL); +#else + return GetStackTrace(stackTrace, outKeepaliveArray, GetThread()); +#endif // DACCESS_COMPILE + } + +private: + static void GetStackTraceClone(StackTraceArray & stackTrace, PTRARRAYREF * outKeepAliveArray); +public: + void GetStackTrace(StackTraceArray & stackTrace, PTRARRAYREF * outKeepaliveArray, Thread *pCurrentThread) const; + + static void GetStackTraceParts(OBJECTREF stackTraceObj, StackTraceArray & stackTrace, PTRARRAYREF * outKeepaliveArray); + + OBJECTREF GetStackTraceArrayObject() const + { + LIMITED_METHOD_DAC_CONTRACT; + return _stackTrace; + } + + void SetInnerException(OBJECTREF innerException) + { + WRAPPER_NO_CONTRACT; + SetObjectReference((OBJECTREF*)&_innerException, (OBJECTREF)innerException); + } + + OBJECTREF GetInnerException() + { + LIMITED_METHOD_DAC_CONTRACT; + return VolatileLoadWithoutBarrierOBJECTREF(&_innerException); + } + + // Returns the innermost exception object - equivalent of the + // managed System.Exception.GetBaseException method. + OBJECTREF GetBaseException() + { + LIMITED_METHOD_CONTRACT; + + // Loop and get the innermost exception object + OBJECTREF oInnerMostException = NULL; + OBJECTREF oCurrent = NULL; + + oCurrent = GetInnerException(); + while(oCurrent != NULL) + { + oInnerMostException = oCurrent; + oCurrent = ((ExceptionObject*)(Object *)OBJECTREFToObject(oCurrent))->GetInnerException(); + } + + // return the innermost exception + return oInnerMostException; + } + + void SetMessage(STRINGREF message) + { + WRAPPER_NO_CONTRACT; + SetObjectReference((OBJECTREF*)&_message, (OBJECTREF)message); + } + + STRINGREF GetMessage() + { + LIMITED_METHOD_DAC_CONTRACT; + return _message; + } + + void SetStackTraceString(STRINGREF stackTraceString) + { + WRAPPER_NO_CONTRACT; + SetObjectReference((OBJECTREF*)&_stackTraceString, (OBJECTREF)stackTraceString); + } + + STRINGREF GetStackTraceString() + { + LIMITED_METHOD_DAC_CONTRACT; + return _stackTraceString; + } + + STRINGREF GetRemoteStackTraceString() + { + LIMITED_METHOD_DAC_CONTRACT; + return (STRINGREF)VolatileLoadWithoutBarrierOBJECTREF(&_remoteStackTraceString); + } + + void SetHelpURL(STRINGREF helpURL) + { + WRAPPER_NO_CONTRACT; + SetObjectReference((OBJECTREF*)&_helpURL, (OBJECTREF)helpURL); + } + + void SetSource(STRINGREF source) + { + WRAPPER_NO_CONTRACT; + SetObjectReference((OBJECTREF*)&_source, (OBJECTREF)source); + } + + void ClearStackTraceForThrow() + { + WRAPPER_NO_CONTRACT; + SetObjectReference((OBJECTREF*)&_remoteStackTraceString, NULL); + SetObjectReference((OBJECTREF*)&_stackTrace, NULL); + SetObjectReference((OBJECTREF*)&_stackTraceString, NULL); + } + + void ClearStackTracePreservingRemoteStackTrace() + { + WRAPPER_NO_CONTRACT; + SetObjectReference((OBJECTREF*)&_stackTrace, NULL); + SetObjectReference((OBJECTREF*)&_stackTraceString, NULL); + } + + // This method will set the reference to the array + // containing the watson bucket information (in byte[] form). + void SetWatsonBucketReference(OBJECTREF oWatsonBucketArray) + { + WRAPPER_NO_CONTRACT; + SetObjectReference((OBJECTREF*)&_watsonBuckets, (OBJECTREF)oWatsonBucketArray); + } + + // This method will return the reference to the array + // containing the watson buckets + U1ARRAYREF GetWatsonBucketReference() + { + LIMITED_METHOD_CONTRACT; + return (U1ARRAYREF)VolatileLoadWithoutBarrierOBJECTREF(&_watsonBuckets); + } + + // This method will return a BOOL to indicate if the + // watson buckets are present or not. + BOOL AreWatsonBucketsPresent() + { + LIMITED_METHOD_CONTRACT; + return (GetWatsonBucketReference() != NULL)?TRUE:FALSE; + } + + // This method will save the IP to be used for watson bucketing. + void SetIPForWatsonBuckets(UINT_PTR ip) + { + LIMITED_METHOD_CONTRACT; + + _ipForWatsonBuckets = ip; + } + + // This method will return a BOOL to indicate if Watson bucketing IP + // is present (or not). + BOOL IsIPForWatsonBucketsPresent() + { + LIMITED_METHOD_CONTRACT; + + return (_ipForWatsonBuckets != 0); + } + + // This method returns the IP for Watson Buckets. + UINT_PTR GetIPForWatsonBuckets() + { + LIMITED_METHOD_CONTRACT; + + return VolatileLoadWithoutBarrier(&_ipForWatsonBuckets); + } + + // README: + // If you modify the order of these fields, make sure to update the definition in + // BCL for this object. +private: + OBJECTREF _exceptionMethod; //Needed for serialization. + STRINGREF _message; + OBJECTREF _data; + OBJECTREF _innerException; + STRINGREF _helpURL; + OBJECTREF _stackTrace; + U1ARRAYREF _watsonBuckets; + STRINGREF _stackTraceString; //Needed for serialization. + STRINGREF _remoteStackTraceString; + STRINGREF _source; // Mainly used by VB. + + UINT_PTR _ipForWatsonBuckets; // Contains the IP of exception for watson bucketing + void* _xptrs; + INT32 _xcode; + INT32 _HResult; + + friend struct ::cdac_data; +}; + +template<> +struct cdac_data +{ + static constexpr size_t _message = offsetof(ExceptionObject, _message); + static constexpr size_t _innerException = offsetof(ExceptionObject, _innerException); + static constexpr size_t _stackTrace = offsetof(ExceptionObject, _stackTrace); + static constexpr size_t _watsonBuckets = offsetof(ExceptionObject, _watsonBuckets); + static constexpr size_t _stackTraceString = offsetof(ExceptionObject, _stackTraceString); + static constexpr size_t _remoteStackTraceString = offsetof(ExceptionObject, _remoteStackTraceString); + static constexpr size_t _HResult = offsetof(ExceptionObject, _HResult); + static constexpr size_t _xcode = offsetof(ExceptionObject, _xcode); +}; + +#include "poppack.h" + +//=============================================================================== +// #NullableFeature +// #NullableArchitecture +// +// In a nutshell it is counterintuitive to have a boxed Nullable, since a boxed +// object already has a representation for null (the null pointer), and having +// multiple representations for the 'not present' value just causes grief. Thus the +// feature is build make Nullable box to a boxed (not boxed). +// +// We want to do this in a way that does not impact the perf of the runtime in the +// non-nullable case. +// +// To do this we need to +// * Make a new helper for the Unbox case (see code:JIT_Unbox_Nullable) +// * Plumb the JIT to ask for what kind of Boxing helper is needed +// (see code:CEEInfo.getBoxHelper, code:CEEInfo.getUnBoxHelper +// * change all the places in the CLR where we box or unbox by hand, and force +// them to use code:MethodTable.Box, and code:MethodTable.Unbox which in +// turn call code:Nullable.Box and code:Nullable.UnBox, most of these +// are in reflection, and remoting (passing and returning value types). +// +// #NullableVerification +// +// Sadly, the IL Verifier also needs to know about this change. Basically the 'box' +// instruction returns a boxed(T) (not a boxed(Nullable)) for the purposes of +// verfication. The JIT finds out what box returns by calling back to the EE with +// the code:CEEInfo.getTypeForBox API. +// +// #NullableDebugging +// +// Sadly, because the debugger also does its own boxing 'by hand' for expression +// evaluation inside visual studio, it measn that debuggers also need to be aware +// of the fact that Nullable boxes to a boxed. It is the responcibility of +// debuggers to follow this convention (which is why this is sad). +// + +//=============================================================================== +// Nullable represents the managed generic value type Nullable +// +// The runtime has special logic for this value class. When it is boxed +// it becomes either null or a boxed T. Similarly a boxed T can be unboxed +// either as a T (as normal), or as a Nullable +// +// See code:Nullable#NullableArchitecture for more. +// +class Nullable { + Nullable(); // This is purposefully undefined. Do not make instances + // of this class. +public: + static void CheckFieldOffsets(TypeHandle nullableType); + static BOOL IsNullableType(TypeHandle nullableType); + static BOOL IsNullableForType(TypeHandle nullableType, MethodTable* paramMT); + + static OBJECTREF Box(void* src, MethodTable* nullable); + static BOOL UnBox(void* dest, OBJECTREF boxedVal, MethodTable* destMT); + static void UnBoxNoCheck(void* dest, OBJECTREF boxedVal, MethodTable* destMT); + static OBJECTREF BoxedNullableNull(TypeHandle nullableType) { return NULL; } + // if 'Obj' is a true boxed nullable, return the form we want (either null or a boxed T) + static OBJECTREF NormalizeBox(OBJECTREF obj); + + static inline CLR_BOOL HasValue(void *src, MethodTable *nullableMT) + { + Nullable *nullable = (Nullable *)src; + return *(nullable->HasValueAddr(nullableMT)); + } + + static inline void *Value(void *src, MethodTable *nullableMT) + { + Nullable *nullable = (Nullable *)src; + return nullable->ValueAddr(nullableMT); + } + + static int32_t GetValueAddrOffset(MethodTable* nullableMT); + +private: + static BOOL IsNullableForTypeHelper(MethodTable* nullableMT, MethodTable* paramMT); + + CLR_BOOL* HasValueAddr(MethodTable* nullableMT); + void* ValueAddr(MethodTable* nullableMT); +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF EXCEPTIONREF; +#else // USE_CHECKED_OBJECTREFS +typedef PTR_ExceptionObject EXCEPTIONREF; +#endif // USE_CHECKED_OBJECTREFS + +#endif // _OBJECT_H_ diff --git a/src/coreclr/vm/object.h.orig b/src/coreclr/vm/object.h.orig new file mode 100644 index 00000000000000..89a1de721ae0d4 --- /dev/null +++ b/src/coreclr/vm/object.h.orig @@ -0,0 +1,2586 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// OBJECT.H +// +// Definitions of a CLR Object +// + +// See code:EEStartup#TableOfContents for overview + + +#ifndef _OBJECT_H_ +#define _OBJECT_H_ + +#include "util.hpp" +#include "syncblk.h" +#include "gcdesc.h" +#include "sstring.h" +#include "daccess.h" +#include "fcall.h" + +extern "C" void __fastcall ZeroMemoryInGCHeap(void*, size_t); + +void ErectWriteBarrierForMT(MethodTable **dst, MethodTable *ref); + +/* + #ObjectModel + * CLR Internal Object Model + * + * + * Object - This is the common base part to all CLR objects + * | it contains the MethodTable pointer and the + * | sync block index, which is at a negative offset + * | + * +-- StringObject - String objects are specialized objects for string + * | storage/retrieval for higher performance (UCS-2 / UTF-16 data) + * | + * +-- ReflectClassBaseObject - The base object for the RuntimeType class + * | + * +-- ReflectMethodObject - The base object for the RuntimeMethodInfo class + * | + * +-- ReflectFieldObject - The base object for the RtFieldInfo class + * | + * +-- ArrayBase - Base portion of all arrays + * | | + * | +- I1Array - Base type SZ arrays + * | | I2Array + * | | ... + * | | + * | +- PtrArray - SZ Array of OBJECTREFs, different than base arrays because of pObjectClass + * | + * +-- AssemblyBaseObject - The base object for the class Assembly + * | + * | ... + * + * + * PLEASE NOTE THE FOLLOWING WHEN ADDING A NEW OBJECT TYPE: + * + * The size of the object in the heap must be able to be computed + * very, very quickly for GC purposes. Restrictions on the layout + * of the object guarantee this is possible. + * + * Any object that inherits from Object must be able to + * compute its complete size by using the first 4 bytes of + * the object following the Object part and constants + * reachable from the MethodTable... + * + * The formula used for this calculation is: + * MT->GetBaseSize() + ((OBJECTTYPEREF->GetSizeField() * MT->GetComponentSize()) + * + * So for Object, since this is of fixed size, the ComponentSize is 0, which makes the right side + * of the equation above equal to 0 no matter what the value of GetSizeField(), so the size is just the base size. + * + */ + +class MethodTable; +class Thread; +class Assembly; +class AssemblyNative; +class WaitHandleNative; +class ArgDestination; + +struct RCW; + +#ifdef TARGET_64BIT +#define OBJHEADER_SIZE (sizeof(DWORD) /* m_alignpad */ + sizeof(DWORD) /* m_SyncBlockValue */) +#else +#define OBJHEADER_SIZE sizeof(DWORD) /* m_SyncBlockValue */ +#endif + +#define OBJECT_SIZE TARGET_POINTER_SIZE /* m_pMethTab */ +#define OBJECT_BASESIZE (OBJHEADER_SIZE + OBJECT_SIZE) + +#ifdef TARGET_64BIT +#define ARRAYBASE_SIZE (OBJECT_SIZE /* m_pMethTab */ + sizeof(DWORD) /* m_NumComponents */ + sizeof(DWORD) /* pad */) +#else +#define ARRAYBASE_SIZE (OBJECT_SIZE /* m_pMethTab */ + sizeof(DWORD) /* m_NumComponents */) +#endif + +#define ARRAYBASE_BASESIZE (OBJHEADER_SIZE + ARRAYBASE_SIZE) + +// +// The generational GC requires that every object be at least 12 bytes +// in size. + +#define MIN_OBJECT_SIZE (2*TARGET_POINTER_SIZE + OBJHEADER_SIZE) + +#define PTRALIGNCONST (DATA_ALIGNMENT-1) + +#ifndef PtrAlign +#define PtrAlign(size) \ + (((size) + PTRALIGNCONST) & (~PTRALIGNCONST)) +#endif //!PtrAlign + +// code:Object is the representation of an managed object on the GC heap. +// +// See code:#ObjectModel for some important subclasses of code:Object +// +// The only fields mandated by all objects are +// +// * a pointer to the code:MethodTable at offset 0 +// * a pointer to a code:ObjHeader at a negative offset. This is often zero. It holds information that +// any addition information that we might need to attach to arbitrary objects. +// +class Object +{ + friend class CheckAsmOffsets; + + protected: + PTR_MethodTable m_pMethTab; + + protected: + Object() { LIMITED_METHOD_CONTRACT; }; + ~Object() { LIMITED_METHOD_CONTRACT; }; + + public: + MethodTable *RawGetMethodTable() const + { + return m_pMethTab; + } + +#ifndef DACCESS_COMPILE + void RawSetMethodTable(MethodTable *pMT) + { + LIMITED_METHOD_CONTRACT; + m_pMethTab = pMT; + } + + VOID SetMethodTable(MethodTable *pMT) + { + WRAPPER_NO_CONTRACT; + RawSetMethodTable(pMT); + } + + VOID SetMethodTableForUOHObject(MethodTable *pMT) + { + WRAPPER_NO_CONTRACT; + // This function must be used if the allocation occurs on a UOH heap, and the method table might be a collectible type + ErectWriteBarrierForMT(&m_pMethTab, pMT); + } +#endif //!DACCESS_COMPILE + +#define MARKED_BIT 0x1 + + PTR_MethodTable GetMethodTable() const + { + LIMITED_METHOD_DAC_CONTRACT; + +#ifndef DACCESS_COMPILE + // We should always use GetGCSafeMethodTable() if we're running during a GC. + // If the mark bit is set then we're running during a GC + _ASSERTE((dac_cast(m_pMethTab) & MARKED_BIT) == 0); + + return m_pMethTab; +#else //DACCESS_COMPILE + + //@dbgtodo dharvey Make this a type which supports bitwise and operations + //when available + return PTR_MethodTable((dac_cast(m_pMethTab)) & (~MARKED_BIT)); +#endif //DACCESS_COMPILE + } + + DPTR(PTR_MethodTable) GetMethodTablePtr() const + { + LIMITED_METHOD_CONTRACT; + return dac_cast(PTR_HOST_MEMBER_TADDR(Object, this, m_pMethTab)); + } + + TypeHandle GetTypeHandle(); + + // Methods used to determine if an object supports a given interface. + static BOOL SupportsInterface(OBJECTREF pObj, MethodTable *pInterfaceMT); + + inline DWORD GetNumComponents(); + inline SIZE_T GetSize(); + + CGCDesc* GetSlotMap() + { + WRAPPER_NO_CONTRACT; + return( CGCDesc::GetCGCDescFromMT(GetMethodTable())); + } + + // Sync Block & Synchronization services + + // Access the ObjHeader which is at a negative offset on the object (because of + // cache lines) + PTR_ObjHeader GetHeader() + { + LIMITED_METHOD_DAC_CONTRACT; + return dac_cast(this) - 1; + } + + // Get the current address of the object (works for debug refs, too.) + PTR_BYTE GetAddress() + { + LIMITED_METHOD_DAC_CONTRACT; + return dac_cast(this); + } + +#ifdef _DEBUG + // TRUE if the header has a real SyncBlockIndex (i.e. it has an entry in the + // SyncTable, though it doesn't necessarily have an entry in the SyncBlockCache) + BOOL HasEmptySyncBlockInfo() + { + WRAPPER_NO_CONTRACT; + return GetHeader()->HasEmptySyncBlockInfo(); + } +#endif + + // retrieve or allocate a sync block for this object + SyncBlock *GetSyncBlock() + { + WRAPPER_NO_CONTRACT; + return GetHeader()->GetSyncBlock(); + } + + DWORD GetSyncBlockIndex() + { + WRAPPER_NO_CONTRACT; + return GetHeader()->GetSyncBlockIndex(); + } + + // DO NOT ADD ANY ASSERTS TO THIS METHOD. + // DO NOT USE THIS METHOD. + // Yes folks, for better or worse the debugger pokes supposed object addresses + // to try to see if objects are valid, possibly firing an AccessViolation or worse, + // and then catches the AV and reports a failure to the debug client. This makes + // the debugger slightly more robust should any corrupted object references appear + // in a session. Thus it is "correct" behaviour for this to AV when used with + // an invalid object pointer, and incorrect behaviour for it to + // assert. + BOOL ValidateObjectWithPossibleAV(); + + // Validate an object ref out of the VerifyHeap routine in the GC + void ValidateHeap(BOOL bDeep=TRUE); + + PTR_SyncBlock PassiveGetSyncBlock() + { + LIMITED_METHOD_DAC_CONTRACT; + return GetHeader()->PassiveGetSyncBlock(); + } + + static DWORD ComputeHashCode(); + static DWORD GetGlobalNewHashCode(); + + inline INT32 TryGetHashCode(); +#ifndef DACCESS_COMPILE + INT32 GetHashCodeEx(); +#endif // #ifndef DACCESS_COMPILE + + PTR_VOID UnBox(); // if it is a value class, get the pointer to the first field + + PTR_BYTE GetData(void) + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + return dac_cast(this) + sizeof(Object); + } + + static UINT GetOffsetOfFirstField() + { + LIMITED_METHOD_CONTRACT; + return sizeof(Object); + } + + DWORD GetOffset32(DWORD dwOffset) + { + WRAPPER_NO_CONTRACT; + return * PTR_DWORD(GetData() + dwOffset); + } + + USHORT GetOffset16(DWORD dwOffset) + { + WRAPPER_NO_CONTRACT; + return * PTR_USHORT(GetData() + dwOffset); + } + + BYTE GetOffset8(DWORD dwOffset) + { + WRAPPER_NO_CONTRACT; + return * PTR_BYTE(GetData() + dwOffset); + } + + int64_t GetOffset64(DWORD dwOffset) + { + WRAPPER_NO_CONTRACT; + return (int64_t) * PTR_ULONG64(GetData() + dwOffset); + } + + void *GetPtrOffset(DWORD dwOffset) + { + WRAPPER_NO_CONTRACT; + return (void *)(TADDR)*PTR_TADDR(GetData() + dwOffset); + } + +#ifndef DACCESS_COMPILE + + void SetOffsetObjectRef(DWORD dwOffset, size_t dwValue); + + void SetOffsetPtr(DWORD dwOffset, LPVOID value) + { + WRAPPER_NO_CONTRACT; + *(LPVOID *) &GetData()[dwOffset] = value; + } + + void SetOffset32(DWORD dwOffset, DWORD dwValue) + { + WRAPPER_NO_CONTRACT; + *(DWORD *) &GetData()[dwOffset] = dwValue; + } + + void SetOffset16(DWORD dwOffset, DWORD dwValue) + { + WRAPPER_NO_CONTRACT; + *(USHORT *) &GetData()[dwOffset] = (USHORT) dwValue; + } + + void SetOffset8(DWORD dwOffset, DWORD dwValue) + { + WRAPPER_NO_CONTRACT; + *(BYTE *) &GetData()[dwOffset] = (BYTE) dwValue; + } + + void SetOffset64(DWORD dwOffset, int64_t qwValue) + { + WRAPPER_NO_CONTRACT; + *(int64_t *) &GetData()[dwOffset] = qwValue; + } + +#endif // #ifndef DACCESS_COMPILE + + VOID Validate(BOOL bDeep = TRUE, BOOL bVerifyNextHeader = TRUE, BOOL bVerifySyncBlock = TRUE); + + PTR_MethodTable GetGCSafeMethodTable() const + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + + // lose GC marking bit and the reserved bit + // A method table pointer should always be aligned. During GC we set the least + // significant bit for marked objects, and the second to least significant + // bit is reserved. So if we want the actual MT pointer during a GC + // we must zero out the lowest 2 bits on 32-bit and 3 bits on 64-bit. +#ifdef TARGET_64BIT + return dac_cast((dac_cast(m_pMethTab)) & ~((UINT_PTR)7)); +#else + return dac_cast((dac_cast(m_pMethTab)) & ~((UINT_PTR)3)); +#endif //TARGET_64BIT + } + + // There are some cases where it is unsafe to get the type handle during a GC. + // This occurs when the type has already been unloaded as part of an in-progress appdomain shutdown. + TypeHandle GetGCSafeTypeHandleIfPossible() const; + + inline TypeHandle GetGCSafeTypeHandle() const; + +#ifdef DACCESS_COMPILE + void EnumMemoryRegions(void); +#endif + + private: + VOID ValidateInner(BOOL bDeep, BOOL bVerifyNextHeader, BOOL bVerifySyncBlock); + + friend struct ::cdac_data; +}; + +template<> +struct cdac_data +{ + static constexpr size_t m_pMethTab = offsetof(Object, m_pMethTab); +}; + +/* + * Object ref setting routines. You must use these to do + * proper write barrier support. + */ + +// SetObjectReference sets an OBJECTREF field + +void SetObjectReferenceUnchecked(OBJECTREF *dst,OBJECTREF ref); + +#ifdef _DEBUG +void EnableStressHeapHelper(); +#endif + +//Used to clear the object reference +inline void ClearObjectReference(OBJECTREF* dst) +{ + LIMITED_METHOD_CONTRACT; + *(void**)(dst) = NULL; +} + +// CopyValueClass sets a value class field + +void CopyValueClassUnchecked(void* dest, void* src, MethodTable *pMT); +void CopyValueClassArgUnchecked(ArgDestination *argDest, void* src, MethodTable *pMT, int destOffset); + +inline void InitValueClass(void *dest, MethodTable *pMT) +{ + WRAPPER_NO_CONTRACT; + ZeroMemoryInGCHeap(dest, pMT->GetNumInstanceFieldBytes()); +} + +// Initialize value class argument +void InitValueClassArg(ArgDestination *argDest, MethodTable *pMT); + +#define SetObjectReference(_d,_r) SetObjectReferenceUnchecked(_d, _r) +#define CopyValueClass(_d,_s,_m) CopyValueClassUnchecked(_d,_s,_m) +#define CopyValueClassArg(_d,_s,_m,_o) CopyValueClassArgUnchecked(_d,_s,_m,_o) + +#include + + +// There are two basic kinds of array layouts in CLR +// ELEMENT_TYPE_ARRAY - a multidimensional array with lower bounds on the dims +// ELMENNT_TYPE_SZARRAY - A zero based single dimensional array +// +// In addition the layout of an array in memory is also affected by +// whether the method table is shared (eg in the case of arrays of object refs) +// or not. In the shared case, the array has to hold the type handle of +// the element type. +// +// ArrayBase encapuslates all of these details. In theory you should never +// have to peek inside this abstraction +// +class ArrayBase : public Object +{ + friend class GCHeap; + friend class CObjectHeader; + friend class Object; + friend OBJECTREF AllocateSzArray(MethodTable *pArrayMT, INT32 length, GC_ALLOC_FLAGS flags); + friend OBJECTREF TryAllocateFrozenSzArray(MethodTable* pArrayMT, INT32 length); + friend class JIT_TrialAlloc; + friend class CheckAsmOffsets; + friend struct _DacGlobals; + +private: + // This MUST be the first field, so that it directly follows Object. This is because + // Object::GetSize() looks at m_NumComponents even though it may not be an array (the + // values is shifted out if not an array, so it's ok). + DWORD m_NumComponents; +#ifdef TARGET_64BIT + DWORD pad; +#endif // TARGET_64BIT + + SVAL_DECL(INT32, s_arrayBoundsZero); // = 0 + + // What comes after this conceputally is: + // INT32 bounds[rank]; The bounds are only present for Multidimensional arrays + // INT32 lowerBounds[rank]; Valid indexes are lowerBounds[i] <= index[i] < lowerBounds[i] + bounds[i] + +public: +#ifndef DACCESS_COMPILE + void SetNumComponents(INT32 length) + { + LIMITED_METHOD_CONTRACT; + m_NumComponents = length; + } +#endif // !DACCESS_COMPILE + + // Get the element type for the array, this works whether the element + // type is stored in the array or not + inline TypeHandle GetArrayElementTypeHandle() const; + + // Get the CorElementType for the elements in the array. Avoids creating a TypeHandle + inline CorElementType GetArrayElementType() const; + + inline unsigned GetRank() const; + + // Total element count for the array + inline DWORD GetNumComponents() const; + + // Get pointer to elements, handles any number of dimensions + PTR_BYTE GetGCSafeDataPtr() const { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; +#ifdef _DEBUG +#ifndef DACCESS_COMPILE + EnableStressHeapHelper(); +#endif +#endif + return dac_cast(this) + + GetDataPtrOffset(GetGCSafeMethodTable()); + } + + PTR_BYTE GetDataPtr() const { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; +#ifdef _DEBUG +#ifndef DACCESS_COMPILE + EnableStressHeapHelper(); +#endif +#endif + PTR_BYTE result = dac_cast(this) + + GetDataPtrOffset(GetMethodTable()); + _ASSERTE(result == GetGCSafeDataPtr()); + return result; + } + + // The component size is actually 16-bit WORD, but this method is returning SIZE_T to ensure + // that SIZE_T is used everywhere for object size computation. It is necessary to support + // objects bigger than 2GB. + SIZE_T GetComponentSize() const { + WRAPPER_NO_CONTRACT; + MethodTable * pMT; + pMT = GetMethodTable(); + _ASSERTE(pMT->HasComponentSize()); + return pMT->RawGetComponentSize(); + } + + // Note that this can be a multidimensional array of rank 1 + // (for example if we had a 1-D array with lower bounds + BOOL IsMultiDimArray() const { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return(GetMethodTable()->IsMultiDimArray()); + } + + // Get pointer to the beginning of the bounds (counts for each dim) + // Works for any array type + PTR_INT32 GetBoundsPtr() const { + WRAPPER_NO_CONTRACT; + MethodTable * pMT = GetMethodTable(); + if (pMT->IsMultiDimArray()) + { + return dac_cast( + dac_cast(this) + sizeof(*this)); + } + else + { + return dac_cast(PTR_HOST_MEMBER_TADDR(ArrayBase, this, + m_NumComponents)); + } + } + + // Works for any array type + PTR_INT32 GetLowerBoundsPtr() const { + WRAPPER_NO_CONTRACT; + if (IsMultiDimArray()) + { + // Lower bounds info is after total bounds info + // and total bounds info has rank elements + return GetBoundsPtr() + GetRank(); + } + else + return dac_cast(GVAL_ADDR(s_arrayBoundsZero)); + } + + static unsigned GetOffsetOfNumComponents() { + LIMITED_METHOD_CONTRACT; + return offsetof(ArrayBase, m_NumComponents); + } + + inline static unsigned GetDataPtrOffset(MethodTable* pMT); + + inline static unsigned GetBoundsOffset(MethodTable* pMT); + inline static unsigned GetLowerBoundsOffset(MethodTable* pMT); + + friend struct ::cdac_data; +}; + +#ifndef DACCESS_COMPILE +template<> +struct cdac_data +{ + static constexpr size_t m_NumComponents = offsetof(ArrayBase, m_NumComponents); + + static constexpr INT32* ArrayBoundsZero = &ArrayBase::s_arrayBoundsZero; +}; +#endif + +// +// Template used to build all the non-object +// arrays of a single dimension +// + +template < class KIND > +class Array : public ArrayBase +{ + public: + + typedef DPTR(KIND) PTR_KIND; + + KIND m_Array[1]; + + PTR_KIND GetDirectPointerToNonObjectElements() const + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return dac_cast(PTR_HOST_MEMBER_TADDR(Array, this, m_Array)); + } +}; + + +// Warning: Use PtrArray only for single dimensional arrays, not multidim arrays. +class PtrArray : public ArrayBase +{ + friend class GCHeap; + friend class ClrDataAccess; + friend class JIT_TrialAlloc; + friend class CheckAsmOffsets; + +public: + TypeHandle GetArrayElementTypeHandle() + { + LIMITED_METHOD_CONTRACT; + return GetMethodTable()->GetArrayElementTypeHandle(); + } + + PTR_OBJECTREF GetDataPtr() + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + return dac_cast(dac_cast(this) + GetDataOffset()); + } + + static SIZE_T GetDataOffset() + { + LIMITED_METHOD_CONTRACT; + return offsetof(PtrArray, m_Array); + } + + void SetAt(SIZE_T i, OBJECTREF ref) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + _ASSERTE(i < GetNumComponents()); + SetObjectReference(m_Array + i, ref); + } + + void ClearAt(SIZE_T i) + { + WRAPPER_NO_CONTRACT; + _ASSERTE(i < GetNumComponents()); + ClearObjectReference(m_Array + i); + } + + OBJECTREF GetAt(SIZE_T i) + { + LIMITED_METHOD_CONTRACT; + SUPPORTS_DAC; + _ASSERTE(i < GetNumComponents()); + +// DAC doesn't know the true size of this array +// the compiler thinks it is size 1, but really it is size N hanging off the structure +#ifndef DACCESS_COMPILE + return m_Array[i]; +#else + TADDR arrayTargetAddress = dac_cast(this) + offsetof(PtrArray, m_Array); + __ArrayDPtr targetArray = dac_cast< __ArrayDPtr >(arrayTargetAddress); + return targetArray[i]; +#endif + } + + friend class StubLinkerCPU; + friend class ArrayOpLinker; +public: + OBJECTREF m_Array[1]; +}; + +#define OFFSETOF__PtrArray__m_Array_ ARRAYBASE_SIZE + +/* a TypedByRef is a structure that is used to implement VB's BYREF variants. + it is basically a tuple of an address of some data along with a TypeHandle + that indicates the type of the address */ +class TypedByRef +{ +public: + + PTR_VOID data; + TypeHandle type; +}; + +typedef DPTR(TypedByRef) PTR_TypedByRef; + +typedef Array I1Array; +typedef Array I2Array; +typedef Array I4Array; +typedef Array I8Array; +typedef Array R4Array; +typedef Array R8Array; +typedef Array U1Array; +typedef Array BOOLArray; +typedef Array U2Array; +typedef Array CHARArray; +typedef Array U4Array; +typedef Array U8Array; +typedef Array UPTRArray; +typedef PtrArray PTRArray; + +typedef DPTR(I1Array) PTR_I1Array; +typedef DPTR(I2Array) PTR_I2Array; +typedef DPTR(I4Array) PTR_I4Array; +typedef DPTR(I8Array) PTR_I8Array; +typedef DPTR(R4Array) PTR_R4Array; +typedef DPTR(R8Array) PTR_R8Array; +typedef DPTR(U1Array) PTR_U1Array; +typedef DPTR(BOOLArray) PTR_BOOLArray; +typedef DPTR(U2Array) PTR_U2Array; +typedef DPTR(CHARArray) PTR_CHARArray; +typedef DPTR(U4Array) PTR_U4Array; +typedef DPTR(U8Array) PTR_U8Array; +typedef DPTR(UPTRArray) PTR_UPTRArray; +typedef DPTR(PTRArray) PTR_PTRArray; + +class StringObject; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF BASEARRAYREF; +typedef REF I1ARRAYREF; +typedef REF I2ARRAYREF; +typedef REF I4ARRAYREF; +typedef REF I8ARRAYREF; +typedef REF R4ARRAYREF; +typedef REF R8ARRAYREF; +typedef REF U1ARRAYREF; +typedef REF BOOLARRAYREF; +typedef REF U2ARRAYREF; +typedef REF U4ARRAYREF; +typedef REF U8ARRAYREF; +typedef REF UPTRARRAYREF; +typedef REF CHARARRAYREF; +typedef REF PTRARRAYREF; // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays. +typedef REF STRINGREF; + +#else // USE_CHECKED_OBJECTREFS + +typedef PTR_ArrayBase BASEARRAYREF; +typedef PTR_I1Array I1ARRAYREF; +typedef PTR_I2Array I2ARRAYREF; +typedef PTR_I4Array I4ARRAYREF; +typedef PTR_I8Array I8ARRAYREF; +typedef PTR_R4Array R4ARRAYREF; +typedef PTR_R8Array R8ARRAYREF; +typedef PTR_U1Array U1ARRAYREF; +typedef PTR_BOOLArray BOOLARRAYREF; +typedef PTR_U2Array U2ARRAYREF; +typedef PTR_U4Array U4ARRAYREF; +typedef PTR_U8Array U8ARRAYREF; +typedef PTR_UPTRArray UPTRARRAYREF; +typedef PTR_CHARArray CHARARRAYREF; +typedef PTR_PTRArray PTRARRAYREF; // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays. +typedef PTR_StringObject STRINGREF; + +#endif // USE_CHECKED_OBJECTREFS + + +#include + + +/* + * StringObject + * + * Special String implementation for performance. + * + * m_StringLength - Length of string in number of WCHARs + * m_FirstChar - The string buffer + * + */ + + +class StringObject : public Object +{ +#ifdef DACCESS_COMPILE + friend class ClrDataAccess; +#endif + friend class GCHeap; + friend class JIT_TrialAlloc; + friend class CheckAsmOffsets; + friend class COMString; + + private: + DWORD m_StringLength; + WCHAR m_FirstChar; + + public: + VOID SetStringLength(DWORD len) { LIMITED_METHOD_CONTRACT; _ASSERTE(len >= 0); m_StringLength = len; } + + protected: + StringObject() {LIMITED_METHOD_CONTRACT; } + ~StringObject() {LIMITED_METHOD_CONTRACT; } + + public: + static DWORD GetBaseSize(); + static SIZE_T GetSize(DWORD stringLength); + + DWORD GetStringLength() { LIMITED_METHOD_DAC_CONTRACT; return( m_StringLength );} + WCHAR* GetBuffer() { LIMITED_METHOD_CONTRACT; return (WCHAR*)( dac_cast(this) + offsetof(StringObject, m_FirstChar) ); } + + static UINT GetBufferOffset() + { + LIMITED_METHOD_DAC_CONTRACT; + return (UINT)(offsetof(StringObject, m_FirstChar)); + } + static UINT GetStringLengthOffset() + { + LIMITED_METHOD_CONTRACT; + return (UINT)(offsetof(StringObject, m_StringLength)); + } + VOID GetSString(SString &result) + { + WRAPPER_NO_CONTRACT; + result.Set(GetBuffer(), GetStringLength()); + } + //======================================================================== + // Creates a System.String object. All the functions that take a length + // or a count of bytes will add the null terminator after length + // characters. So this means that if you have a string that has 5 + // characters and the null terminator you should pass in 5 and NOT 6. + //======================================================================== + static STRINGREF NewString(int length); + static STRINGREF NewString(const WCHAR *pwsz); + static STRINGREF NewString(const WCHAR *pwsz, int length); + static STRINGREF NewString(LPCUTF8 psz); + static STRINGREF NewString(LPCUTF8 psz, int cBytes); + + static STRINGREF GetEmptyString(); + static STRINGREF* GetEmptyStringRefPtr(void** pinnedString); + + static STRINGREF* InitEmptyStringRefPtr(); + + /*=================RefInterpretGetStringValuesDangerousForGC====================== + **N.B.: This performs no range checking and relies on the caller to have done this. + **Args: (IN)ref -- the String to be interpretted. + ** (OUT)chars -- a pointer to the characters in the buffer. + ** (OUT)length -- a pointer to the length of the buffer. + **Returns: void. + **Exceptions: None. + ==============================================================================*/ + // !!!! If you use this function, you have to be careful because chars is a pointer + // !!!! to the data buffer of ref. If GC happens after this call, you need to make + // !!!! sure that you have a pin handle on ref, or use GCPROTECT_BEGINPINNING on ref. + void RefInterpretGetStringValuesDangerousForGC(_Outptr_result_buffer_(*length + 1) WCHAR **chars, int *length) { + WRAPPER_NO_CONTRACT; + + _ASSERTE(GetGCSafeMethodTable() == g_pStringClass); + *length = GetStringLength(); + *chars = GetBuffer(); +#ifdef _DEBUG + EnableStressHeapHelper(); +#endif + } + + +private: + static STRINGREF* EmptyStringRefPtr; + static bool EmptyStringIsFrozen; + + friend struct ::cdac_data; +}; + +template<> +struct cdac_data +{ + static constexpr size_t m_FirstChar = offsetof(StringObject, m_FirstChar); + static constexpr size_t m_StringLength = offsetof(StringObject, m_StringLength); +}; + +/*================================GetEmptyString================================ +**Get a reference to the empty string. If we haven't already gotten one, we +**query the String class for a pointer to the empty string that we know was +**created at startup. +** +**Args: None +**Returns: A STRINGREF to the EmptyString +**Exceptions: None +==============================================================================*/ +inline STRINGREF StringObject::GetEmptyString() { + + CONTRACTL { + THROWS; + MODE_COOPERATIVE; + GC_TRIGGERS; + } CONTRACTL_END; + STRINGREF* refptr = EmptyStringRefPtr; + + //If we've never gotten a reference to the EmptyString, we need to go get one. + if (refptr==NULL) { + refptr = InitEmptyStringRefPtr(); + } + //We've already have a reference to the EmptyString, so we can just return it. + return *refptr; +} + +inline STRINGREF* StringObject::GetEmptyStringRefPtr(void** pinnedString) { + + CONTRACTL { + THROWS; + MODE_ANY; + GC_TRIGGERS; + } CONTRACTL_END; + + STRINGREF* refptr = EmptyStringRefPtr; + + //If we've never gotten a reference to the EmptyString, we need to go get one. + if (refptr == nullptr) + { + refptr = InitEmptyStringRefPtr(); + } + + if (EmptyStringIsFrozen && pinnedString != nullptr) + { + *pinnedString = *(void**)refptr; + } + + //We've already have a reference to the EmptyString, so we can just return it. + return refptr; +} + +// This is the Class version of the Reflection object. +// A Class has adddition information. +// For a ReflectClassBaseObject the m_pData is a pointer to a FieldDesc array that +// contains all of the final static primitives if its defined. +// m_cnt = the number of elements defined in the m_pData FieldDesc array. -1 means +// this hasn't yet been defined. +class ReflectClassBaseObject : public Object +{ + friend class CoreLibBinder; + +protected: + OBJECTREF m_keepalive; + OBJECTREF m_cache; + TypeHandle m_typeHandle; + +#ifdef _DEBUG + void TypeCheck() + { + CONTRACTL + { + NOTHROW; + MODE_COOPERATIVE; + GC_NOTRIGGER; + } + CONTRACTL_END; + + MethodTable *pMT = GetMethodTable(); + while (pMT != g_pRuntimeTypeClass && pMT != NULL) + { + pMT = pMT->GetParentMethodTable(); + } + _ASSERTE(pMT == g_pRuntimeTypeClass); + } +#endif // _DEBUG + +public: + void SetType(TypeHandle type) { + CONTRACTL + { + NOTHROW; + MODE_COOPERATIVE; + GC_NOTRIGGER; + } + CONTRACTL_END; + + INDEBUG(TypeCheck()); + m_typeHandle = type; + } + + void SetKeepAlive(OBJECTREF keepalive) + { + CONTRACTL + { + NOTHROW; + MODE_COOPERATIVE; + GC_NOTRIGGER; + } + CONTRACTL_END; + + INDEBUG(TypeCheck()); + SetObjectReference(&m_keepalive, keepalive); + } + + TypeHandle GetType() { + CONTRACTL + { + NOTHROW; + MODE_COOPERATIVE; + GC_NOTRIGGER; + } + CONTRACTL_END; + + INDEBUG(TypeCheck()); + return m_typeHandle; + } + +}; + +// This is the Method version of the Reflection object. +// A Method has additional information: +// m_pMD - A pointer to the actual MethodDesc of the method. +// m_object - a field that has a reference type in it. Used only for RuntimeMethodInfoStub to keep the real type alive. +// This structure matches the structure up to the m_pMD for several different managed types. +// (RuntimeConstructorInfo, RuntimeMethodInfo, and RuntimeMethodInfoStub). These types are unrelated in the type +// system except that they all implement a particular interface. It is important that such interface is not attached to any +// type that does not sufficiently match this data structure. +class ReflectMethodObject : public Object +{ + friend class CoreLibBinder; + +protected: + OBJECTREF m_object; + OBJECTREF m_empty1; + OBJECTREF m_empty2; + OBJECTREF m_empty3; + OBJECTREF m_empty4; + OBJECTREF m_empty5; + OBJECTREF m_empty6; + OBJECTREF m_empty7; + OBJECTREF m_empty8; + MethodDesc * m_pMD; + +public: + void SetMethod(MethodDesc *pMethod) { + LIMITED_METHOD_CONTRACT; + m_pMD = pMethod; + } + + // This must only be called on instances of ReflectMethodObject that are actually RuntimeMethodInfoStub + void SetKeepAlive(OBJECTREF keepalive) + { + WRAPPER_NO_CONTRACT; + SetObjectReference(&m_object, keepalive); + } + + MethodDesc *GetMethod() { + LIMITED_METHOD_CONTRACT; + return m_pMD; + } + +}; + +// This is the Field version of the Reflection object. +// A Method has additional information: +// m_pFD - A pointer to the actual MethodDesc of the method. +// m_object - a field that has a reference type in it. Used only for RuntimeFieldInfoStub to keep the real type alive. +// This structure matches the structure up to the m_pFD for several different managed types. +// (RtFieldInfo and RuntimeFieldInfoStub). These types are unrelated in the type +// system except that they all implement a particular interface. It is important that such interface is not attached to any +// type that does not sufficiently match this data structure. +class ReflectFieldObject : public Object +{ + friend class CoreLibBinder; + +protected: + OBJECTREF m_object; + OBJECTREF m_empty1; + INT32 m_empty2; + OBJECTREF m_empty3; + OBJECTREF m_empty4; + OBJECTREF m_empty5; + FieldDesc * m_pFD; + +public: + void SetField(FieldDesc *pField) { + LIMITED_METHOD_CONTRACT; + m_pFD = pField; + } + + // This must only be called on instances of ReflectFieldObject that are actually RuntimeFieldInfoStub + void SetKeepAlive(OBJECTREF keepalive) + { + WRAPPER_NO_CONTRACT; + SetObjectReference(&m_object, keepalive); + } + + FieldDesc *GetField() { + LIMITED_METHOD_CONTRACT; + return m_pFD; + } +}; + +// ReflectModuleBaseObject +// This class is the base class for managed Module. +// This class will connect the Object back to the underlying VM representation +// m_ReflectClass -- This is the real Class that was used for reflection +// This class was used to get at this object +// m_pData -- this is a generic pointer which usually points CorModule +// +class ReflectModuleBaseObject : public Object +{ + friend class CoreLibBinder; + + protected: + // READ ME: + // Modifying the order or fields of this object may require other changes to the + // classlib class definition of this object. + OBJECTREF m_runtimeType; + OBJECTREF m_runtimeAssembly; + Module* m_pData; // Pointer to the Module + + protected: + ReflectModuleBaseObject() {LIMITED_METHOD_CONTRACT;} + ~ReflectModuleBaseObject() {LIMITED_METHOD_CONTRACT;} + + public: + void SetModule(Module* p) { + LIMITED_METHOD_CONTRACT; + m_pData = p; + } + Module* GetModule() { + LIMITED_METHOD_CONTRACT; + return m_pData; + } + void SetAssembly(OBJECTREF assembly) + { + WRAPPER_NO_CONTRACT; + SetObjectReference(&m_runtimeAssembly, assembly); + } +}; + +class ThreadBaseObject; +class ExecutionContextObject: public Object +{ + friend class CoreLibBinder; + +private: + // These field are also defined in the managed representation. (SecurityContext.cs)If you + // add or change these field you must also change the managed code so that + // it matches these. This is necessary so that the object is the proper + // size. + OBJECTREF m_localValues; + OBJECTREF m_localChangeNotifications; + CLR_BOOL m_isFlowSuppressed; + +public: + bool IsFlowSuppressed() const + { + LIMITED_METHOD_CONTRACT; + return m_isFlowSuppressed; + } +}; + +typedef DPTR(class CultureInfoBaseObject) PTR_CultureInfoBaseObject; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF EXECUTIONCONTEXTREF; +typedef REF ARRAYBASEREF; + +#else +typedef ExecutionContextObject* EXECUTIONCONTEXTREF; +typedef PTR_ArrayBase ARRAYBASEREF; +#endif + +typedef DPTR(class ThreadBaseObject) PTR_ThreadBaseObject; +class ThreadBaseObject : public Object +{ + friend class ClrDataAccess; + friend class CoreLibBinder; + friend class Object; + +private: + + // These field are also defined in the managed representation. If you + // add or change these field you must also change the managed code so that + // it matches these. This is necessary so that the object is the proper + // size. The order here must match that order which the loader will choose + // when laying out the managed class. Note that the layouts are checked + // at run time, not compile time. + OBJECTREF m_ExecutionContext; + OBJECTREF m_SynchronizationContext; + STRINGREF m_Name; + OBJECTREF m_StartHelper; +#ifdef TARGET_UNIX + OBJECTREF m_WaitInfo; + OBJECTREF m_joinEvent; +#endif // TARGET_UNIX + + // The next field (m_InternalThread) is declared as IntPtr in the managed + // definition of Thread. The loader will sort it next. + + // m_InternalThread is always valid -- unless the thread has finalized and been + // resurrected. (The thread stopped running before it was finalized). + Thread *m_InternalThread; + INT32 m_Priority; + + // We need to cache the thread id in managed code for perf reasons. + INT32 m_ManagedThreadId; + + // Only used by managed code, see comment there + bool m_MayNeedResetForThreadPool; + + // Set in unmanaged code and read in managed code. + bool m_IsDead; + + bool m_IsThreadPool; + +protected: + // the ctor and dtor can do no useful work. + ThreadBaseObject() {LIMITED_METHOD_CONTRACT;}; + ~ThreadBaseObject() {LIMITED_METHOD_CONTRACT;}; + +public: + Thread *GetInternal() + { + LIMITED_METHOD_CONTRACT; + return m_InternalThread; + } + + void SetInternal(Thread *it); + void ClearInternal(); + + void SetManagedThreadId(INT32 id) + { + LIMITED_METHOD_CONTRACT; + m_ManagedThreadId = id; + } + + STRINGREF GetName() { + LIMITED_METHOD_CONTRACT; + return m_Name; + } + + OBJECTREF GetExecutionContext() + { + LIMITED_METHOD_CONTRACT; + return m_ExecutionContext; + } + + void InitExisting(); + + void ResetStartHelper() + { + LIMITED_METHOD_CONTRACT + m_StartHelper = NULL; + } + + void SetPriority(INT32 priority) + { + LIMITED_METHOD_CONTRACT; + m_Priority = priority; + } + + INT32 GetPriority() const + { + LIMITED_METHOD_CONTRACT; + return m_Priority; + } + + void SetIsDead() + { + LIMITED_METHOD_CONTRACT; + m_IsDead = true; + } +}; + +// AssemblyBaseObject +// This class is the base class for assemblies +// +class AssemblyBaseObject : public Object +{ + friend class Assembly; + friend class CoreLibBinder; + + protected: + // READ ME: + // Modifying the order or fields of this object may require other changes to the + // classlib class definition of this object. + OBJECTREF m_pModuleEventHandler; // Delegate for 'resolve module' event + STRINGREF m_fullname; // Slot for storing assemblies fullname + OBJECTREF m_pSyncRoot; // Pointer to loader allocator to keep collectible types alive, and to serve as the syncroot for assembly building in ref.emit + Assembly* m_pAssembly; // Pointer to the Assembly Structure + + protected: + AssemblyBaseObject() { LIMITED_METHOD_CONTRACT; } + ~AssemblyBaseObject() { LIMITED_METHOD_CONTRACT; } + + public: + + void SetAssembly(Assembly* p) + { + LIMITED_METHOD_CONTRACT; + m_pAssembly = p; + } + + Assembly* GetAssembly(); + + void SetSyncRoot(OBJECTREF pSyncRoot) + { + WRAPPER_NO_CONTRACT; + SetObjectReference(&m_pSyncRoot, pSyncRoot); + } +}; + +// AssemblyLoadContextBaseObject +// This class is the base class for AssemblyLoadContext +// +#if defined(TARGET_X86) && !defined(TARGET_UNIX) +#include "pshpack4.h" +#endif // defined(TARGET_X86) && !defined(TARGET_UNIX) +class AssemblyLoadContextBaseObject : public Object +{ + friend class CoreLibBinder; + + protected: + // READ ME: + // Modifying the order or fields of this object may require other changes to the + // classlib class definition of this object. +#ifdef TARGET_64BIT + OBJECTREF _unloadLock; + OBJECTREF _resolvingUnmanagedDll; + OBJECTREF _resolving; + OBJECTREF _unloading; + OBJECTREF _name; + INT_PTR _nativeAssemblyLoadContext; + int64_t _id; // On 64-bit platforms this is a value type so it is placed after references and pointers + DWORD _state; + CLR_BOOL _isCollectible; +#else // TARGET_64BIT + int64_t _id; // On 32-bit platforms this 64-bit value type is larger than a pointer so JIT places it first + OBJECTREF _unloadLock; + OBJECTREF _resolvingUnmanagedDll; + OBJECTREF _resolving; + OBJECTREF _unloading; + OBJECTREF _name; + INT_PTR _nativeAssemblyLoadContext; + DWORD _state; + CLR_BOOL _isCollectible; +#endif // TARGET_64BIT + + protected: + AssemblyLoadContextBaseObject() { LIMITED_METHOD_CONTRACT; } + ~AssemblyLoadContextBaseObject() { LIMITED_METHOD_CONTRACT; } + + public: + INT_PTR GetNativeAssemblyBinder() { LIMITED_METHOD_CONTRACT; return _nativeAssemblyLoadContext; } +}; +#if defined(TARGET_X86) && !defined(TARGET_UNIX) +#include "poppack.h" +#endif // defined(TARGET_X86) && !defined(TARGET_UNIX) + +struct NativeAssemblyNameParts +{ + PCWSTR _pName; + UINT16 _major, _minor, _build, _revision; + PCWSTR _pCultureName; + BYTE* _pPublicKeyOrToken; + int _cbPublicKeyOrToken; + DWORD _flags; +}; + +// AssemblyNameBaseObject +// This class is the base class for assembly names +// +class AssemblyNameBaseObject : public Object +{ + // Dummy definition +}; + +class WeakReferenceObject : public Object +{ +public: + uintptr_t m_taggedHandle; +}; + + +typedef DPTR(class ContinuationObject) PTR_ContinuationObject; +class ContinuationObject; + +#ifdef USE_CHECKED_OBJECTREFS + +typedef REF REFLECTMODULEBASEREF; + +typedef REF REFLECTCLASSBASEREF; + +typedef REF REFLECTMETHODREF; + +typedef REF REFLECTFIELDREF; + +typedef REF THREADBASEREF; + +typedef REF ASSEMBLYREF; + +typedef REF ASSEMBLYLOADCONTEXTREF; + +typedef REF ASSEMBLYNAMEREF; + +typedef REF THREADBASEREF; + +typedef REF CONTINUATIONREF; + +inline ARG_SLOT ObjToArgSlot(OBJECTREF objRef) +{ + LIMITED_METHOD_CONTRACT; + LPVOID v; + v = OBJECTREFToObject(objRef); + return (ARG_SLOT)(SIZE_T)v; +} + +inline OBJECTREF ArgSlotToObj(ARG_SLOT i) +{ + LIMITED_METHOD_CONTRACT; + LPVOID v; + v = (LPVOID)(SIZE_T)i; + return ObjectToOBJECTREF ((Object*)v); +} + +inline ARG_SLOT StringToArgSlot(STRINGREF sr) +{ + LIMITED_METHOD_CONTRACT; + LPVOID v; + v = OBJECTREFToObject(sr); + return (ARG_SLOT)(SIZE_T)v; +} + +inline STRINGREF ArgSlotToString(ARG_SLOT s) +{ + LIMITED_METHOD_CONTRACT; + LPVOID v; + v = (LPVOID)(SIZE_T)s; + return ObjectToSTRINGREF ((StringObject*)v); +} + +#else // USE_CHECKED_OBJECTREFS + +typedef PTR_ReflectModuleBaseObject REFLECTMODULEBASEREF; +typedef PTR_ReflectClassBaseObject REFLECTCLASSBASEREF; +typedef PTR_ReflectMethodObject REFLECTMETHODREF; +typedef PTR_ReflectFieldObject REFLECTFIELDREF; +typedef PTR_ThreadBaseObject THREADBASEREF; +typedef PTR_AssemblyBaseObject ASSEMBLYREF; +typedef PTR_AssemblyLoadContextBaseObject ASSEMBLYLOADCONTEXTREF; +typedef PTR_AssemblyNameBaseObject ASSEMBLYNAMEREF; +typedef PTR_ThreadBaseObject THREADBASEREF; +typedef PTR_ContinuationObject CONTINUATIONREF; + +#define ObjToArgSlot(objref) ((ARG_SLOT)(SIZE_T)(objref)) +#define ArgSlotToObj(s) ((OBJECTREF)(SIZE_T)(s)) + +#define StringToArgSlot(objref) ((ARG_SLOT)(SIZE_T)(objref)) +#define ArgSlotToString(s) ((STRINGREF)(SIZE_T)(s)) + +#endif //USE_CHECKED_OBJECTREFS + +#define PtrToArgSlot(ptr) ((ARG_SLOT)(SIZE_T)(ptr)) +#define ArgSlotToPtr(s) ((LPVOID)(SIZE_T)(s)) + +#define BoolToArgSlot(b) ((ARG_SLOT)(CLR_BOOL)(!!(b))) +#define ArgSlotToBool(s) ((BOOL)(s)) + +STRINGREF AllocateString(SString sstr); + +#ifdef FEATURE_COMINTEROP + +//------------------------------------------------------------- +// class ComObject, Exposed class __ComObject +// +// +//------------------------------------------------------------- +class ComObject : public Object +{ + friend class CoreLibBinder; + +protected: + + ComObject() + {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly + ~ComObject(){LIMITED_METHOD_CONTRACT;}; + +public: + OBJECTREF m_ObjectToDataMap; + + //-------------------------------------------------------------------- + // SupportsInterface + static BOOL SupportsInterface(OBJECTREF oref, MethodTable* pIntfTable); + + //-------------------------------------------------------------------- + // SupportsInterface + static void ThrowInvalidCastException(OBJECTREF *pObj, MethodTable* pCastToMT); + + //----------------------------------------------------------------- + // GetComIPFromRCW + static IUnknown* GetComIPFromRCW(OBJECTREF *pObj, MethodTable* pIntfTable); + + //----------------------------------------------------------------- + // GetComIPFromRCWThrowing + static IUnknown* GetComIPFromRCWThrowing(OBJECTREF *pObj, MethodTable* pIntfTable); + + //----------------------------------------------------------- + // create an empty ComObjectRef + static OBJECTREF CreateComObjectRef(MethodTable* pMT); + + //----------------------------------------------------------- + // Release all the data associated with the __ComObject. + static void ReleaseAllData(OBJECTREF oref); +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF COMOBJECTREF; +#else +typedef ComObject* COMOBJECTREF; +#endif + + +//------------------------------------------------------------- +// class UnknownWrapper, Exposed class UnknownWrapper +// +// +//------------------------------------------------------------- +class UnknownWrapper : public Object +{ +protected: + + UnknownWrapper(UnknownWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. + UnknownWrapper() {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly + ~UnknownWrapper() {LIMITED_METHOD_CONTRACT;}; + + OBJECTREF m_WrappedObject; + +public: + OBJECTREF GetWrappedObject() + { + LIMITED_METHOD_CONTRACT; + return m_WrappedObject; + } + + void SetWrappedObject(OBJECTREF pWrappedObject) + { + LIMITED_METHOD_CONTRACT; + m_WrappedObject = pWrappedObject; + } +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF UNKNOWNWRAPPEROBJECTREF; +#else +typedef UnknownWrapper* UNKNOWNWRAPPEROBJECTREF; +#endif + + +//------------------------------------------------------------- +// class DispatchWrapper, Exposed class DispatchWrapper +// +// +//------------------------------------------------------------- +class DispatchWrapper : public Object +{ +protected: + + DispatchWrapper(DispatchWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. + DispatchWrapper() {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly + ~DispatchWrapper() {LIMITED_METHOD_CONTRACT;}; + + OBJECTREF m_WrappedObject; + +public: + OBJECTREF GetWrappedObject() + { + LIMITED_METHOD_CONTRACT; + return m_WrappedObject; + } + + void SetWrappedObject(OBJECTREF pWrappedObject) + { + LIMITED_METHOD_CONTRACT; + m_WrappedObject = pWrappedObject; + } +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF DISPATCHWRAPPEROBJECTREF; +#else +typedef DispatchWrapper* DISPATCHWRAPPEROBJECTREF; +#endif + + +//------------------------------------------------------------- +// class VariantWrapper, Exposed class VARIANTWRAPPEROBJECTREF +// +// +//------------------------------------------------------------- +class VariantWrapper : public Object +{ +protected: + + VariantWrapper(VariantWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. + VariantWrapper() {LIMITED_METHOD_CONTRACT}; // don't instantiate this class directly + ~VariantWrapper() {LIMITED_METHOD_CONTRACT}; + + OBJECTREF m_WrappedObject; + +public: + OBJECTREF GetWrappedObject() + { + LIMITED_METHOD_CONTRACT; + return m_WrappedObject; + } + + void SetWrappedObject(OBJECTREF pWrappedObject) + { + LIMITED_METHOD_CONTRACT; + m_WrappedObject = pWrappedObject; + } +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF VARIANTWRAPPEROBJECTREF; +#else +typedef VariantWrapper* VARIANTWRAPPEROBJECTREF; +#endif + + +//------------------------------------------------------------- +// class ErrorWrapper, Exposed class ErrorWrapper +// +// +//------------------------------------------------------------- +class ErrorWrapper : public Object +{ +protected: + + ErrorWrapper(ErrorWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. + ErrorWrapper() {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly + ~ErrorWrapper() {LIMITED_METHOD_CONTRACT;}; + + INT32 m_ErrorCode; + +public: + INT32 GetErrorCode() + { + LIMITED_METHOD_CONTRACT; + return m_ErrorCode; + } + + void SetErrorCode(int ErrorCode) + { + LIMITED_METHOD_CONTRACT; + m_ErrorCode = ErrorCode; + } +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF ERRORWRAPPEROBJECTREF; +#else +typedef ErrorWrapper* ERRORWRAPPEROBJECTREF; +#endif + + +//------------------------------------------------------------- +// class CurrencyWrapper, Exposed class CurrencyWrapper +// +// +//------------------------------------------------------------- + +// Keep this in sync with code:MethodTableBuilder.CheckForSystemTypes where +// alignment requirement of the managed System.Decimal structure is computed. +#if !defined(ALIGN_ACCESS) && !defined(FEATURE_64BIT_ALIGNMENT) +#include +#endif // !ALIGN_ACCESS && !FEATURE_64BIT_ALIGNMENT + +class CurrencyWrapper : public Object +{ +protected: + + CurrencyWrapper(CurrencyWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. + CurrencyWrapper() {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly + ~CurrencyWrapper() {LIMITED_METHOD_CONTRACT;}; + + DECIMAL m_WrappedObject; + +public: + DECIMAL GetWrappedObject() + { + LIMITED_METHOD_CONTRACT; + return m_WrappedObject; + } + + void SetWrappedObject(DECIMAL WrappedObj) + { + LIMITED_METHOD_CONTRACT; + m_WrappedObject = WrappedObj; + } +}; + +#if !defined(ALIGN_ACCESS) && !defined(FEATURE_64BIT_ALIGNMENT) +#include +#endif // !ALIGN_ACCESS && !FEATURE_64BIT_ALIGNMENT + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF CURRENCYWRAPPEROBJECTREF; +#else +typedef CurrencyWrapper* CURRENCYWRAPPEROBJECTREF; +#endif + +//------------------------------------------------------------- +// class BStrWrapper, Exposed class BSTRWRAPPEROBJECTREF +// +// +//------------------------------------------------------------- +class BStrWrapper : public Object +{ +protected: + + BStrWrapper(BStrWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. + BStrWrapper() {LIMITED_METHOD_CONTRACT}; // don't instantiate this class directly + ~BStrWrapper() {LIMITED_METHOD_CONTRACT}; + + STRINGREF m_WrappedObject; + +public: + STRINGREF GetWrappedObject() + { + LIMITED_METHOD_CONTRACT; + return m_WrappedObject; + } + + void SetWrappedObject(STRINGREF pWrappedObject) + { + LIMITED_METHOD_CONTRACT; + m_WrappedObject = pWrappedObject; + } +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF BSTRWRAPPEROBJECTREF; +#else +typedef BStrWrapper* BSTRWRAPPEROBJECTREF; +#endif + +#endif // FEATURE_COMINTEROP + + +// This class corresponds to System.MulticastDelegate on the managed side. +class DelegateObject : public Object +{ + friend class CheckAsmOffsets; + friend class CoreLibBinder; + friend struct ::cdac_data; + +public: + BOOL IsWrapperDelegate() { LIMITED_METHOD_CONTRACT; return _methodPtrAux == 0; } + + OBJECTREF GetTarget() { LIMITED_METHOD_CONTRACT; return _target; } + void SetTarget(OBJECTREF target) { WRAPPER_NO_CONTRACT; SetObjectReference(&_target, target); } + static int GetOffsetOfTarget() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _target); } + + MethodDesc* GetMethodDesc() { LIMITED_METHOD_CONTRACT; return _methodDesc; } + void SetMethodDesc(MethodDesc* methodDesc) { LIMITED_METHOD_CONTRACT; _methodDesc = methodDesc; } + static int GetOffsetOfMethodDesc() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodDesc); } + + PCODE GetMethodPtr() { LIMITED_METHOD_CONTRACT; return _methodPtr; } + void SetMethodPtr(PCODE methodPtr) { LIMITED_METHOD_CONTRACT; _methodPtr = methodPtr; } + static int GetOffsetOfMethodPtr() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtr); } + + PCODE GetMethodPtrAux() { LIMITED_METHOD_CONTRACT; return _methodPtrAux; } + void SetMethodPtrAux(PCODE methodPtrAux) { LIMITED_METHOD_CONTRACT; _methodPtrAux = methodPtrAux; } + static int GetOffsetOfMethodPtrAux() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtrAux); } + + OBJECTREF GetInvocationList() { LIMITED_METHOD_CONTRACT; return _invocationList; } + void SetInvocationList(OBJECTREF invocationList) { WRAPPER_NO_CONTRACT; SetObjectReference(&_invocationList, invocationList); } + static int GetOffsetOfInvocationList() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _invocationList); } + + INT_PTR GetInvocationCount() { LIMITED_METHOD_CONTRACT; return _invocationCount; } + void SetInvocationCount(INT_PTR invocationCount) { LIMITED_METHOD_CONTRACT; _invocationCount = invocationCount; } + static int GetOffsetOfInvocationCount() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _invocationCount); } + + // README: + // If you modify the order of these fields, make sure to update the definition in + // BCL for this object. +private: + // System.Delegate +<<<<<<< HEAD + OBJECTREF _target; + MethodDesc* _methodDesc; +======= + OBJECTREF _methodBase; + OBJECTREF _target; +>>>>>>> upstream/main + PCODE _methodPtr; + PCODE _methodPtrAux; + // System.MulticastDelegate + OBJECTREF _invocationList; + INT_PTR _invocationCount; +}; + +<<<<<<< HEAD +#define OFFSETOF__DelegateObject__target OBJECT_SIZE /* m_pMethTab */ +#define OFFSETOF__DelegateObject__methodDesc (OFFSETOF__DelegateObject__target + TARGET_POINTER_SIZE /* _target */) +#define OFFSETOF__DelegateObject__methodPtr (OFFSETOF__DelegateObject__methodDesc + TARGET_POINTER_SIZE /* _methodDesc */) +======= +#define OFFSETOF__DelegateObject__target (OBJECT_SIZE /* m_pMethTab */ + TARGET_POINTER_SIZE /* _methodBase */) +#define OFFSETOF__DelegateObject__methodPtr (OFFSETOF__DelegateObject__target + TARGET_POINTER_SIZE /* _target */) +>>>>>>> upstream/main +#define OFFSETOF__DelegateObject__methodPtrAux (OFFSETOF__DelegateObject__methodPtr + TARGET_POINTER_SIZE /* _methodPtr */) + +template<> +struct cdac_data +{ + static constexpr size_t Target = offsetof(DelegateObject, _target); + static constexpr size_t MethodPtr = offsetof(DelegateObject, _methodPtr); + static constexpr size_t MethodPtrAux = offsetof(DelegateObject, _methodPtrAux); + static constexpr size_t InvocationCount = offsetof(DelegateObject, _invocationCount); +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF DELEGATEREF; +#else // USE_CHECKED_OBJECTREFS +typedef DelegateObject* DELEGATEREF; +#endif // USE_CHECKED_OBJECTREFS + + +struct StackTraceElement; +class ClrDataAccess; + + +typedef DPTR(StackTraceElement) PTR_StackTraceElement; + +class StackTraceArray +{ + struct ArrayHeader + { + uint32_t m_size; + uint32_t m_keepAliveItemsCount; + Thread * m_thread; + }; + + typedef DPTR(ArrayHeader) PTR_ArrayHeader; + +public: + StackTraceArray() + : m_array(static_cast(NULL)) + { + WRAPPER_NO_CONTRACT; + } + + StackTraceArray(I1ARRAYREF array) + : m_array(array) + { + LIMITED_METHOD_CONTRACT; + } + + void Set(I1ARRAYREF array) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + } + CONTRACTL_END; + SUPPORTS_DAC; + m_array = array; + } + + uint32_t Size() const + { + WRAPPER_NO_CONTRACT; + if (!m_array) + return 0; + else + return GetSize(); + } + + StackTraceElement const & operator[](size_t index) const; + StackTraceElement & operator[](size_t index); + + size_t Capacity() const; + void Append(StackTraceElement const * element); + void Allocate(size_t size); + + I1ARRAYREF Get() const + { + LIMITED_METHOD_DAC_CONTRACT; + return m_array; + } + + uint32_t CopyDataFrom(StackTraceArray const & src); + + Thread * GetObjectThread() const + { + WRAPPER_NO_CONTRACT; + return GetHeader()->m_thread; + } + + void SetSize(uint32_t size) + { + WRAPPER_NO_CONTRACT; + VolatileStore(&GetHeader()->m_size, size); + } + + void SetKeepAliveItemsCount(uint32_t count) + { + WRAPPER_NO_CONTRACT; + VolatileStore(&GetHeader()->m_keepAliveItemsCount, count); + } + + uint32_t GetKeepAliveItemsCount() const + { + WRAPPER_NO_CONTRACT; + return VolatileLoad(&GetHeader()->m_keepAliveItemsCount); + } + + // Compute the number of methods in the stack trace that can be collected. We need to store keepAlive + // objects (Resolver / LoaderAllocator) for these methods. + uint32_t ComputeKeepAliveItemsCount(); + + void MarkAsFrozen() + { + if (m_array != NULL) + { + GetHeader()->m_thread = (Thread *)(size_t)1; + } + } + + bool IsFrozen() const + { + return GetHeader()->m_thread == (Thread *)(size_t)1; + } + +private: + StackTraceArray(StackTraceArray const & rhs) = delete; + + StackTraceArray & operator=(StackTraceArray const & rhs) = delete; + + void CheckState() const; + + uint32_t GetSize() const + { + WRAPPER_NO_CONTRACT; + return VolatileLoad(&GetHeader()->m_size); + } + + void SetObjectThread() + { + WRAPPER_NO_CONTRACT; + GetHeader()->m_thread = GetThread(); + } + + StackTraceElement const * GetData() const + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return dac_cast(GetRaw() + sizeof(ArrayHeader)); + } + + PTR_StackTraceElement GetData() + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return dac_cast(GetRaw() + sizeof(ArrayHeader)); + } + + CLR_I1 const * GetRaw() const + { + WRAPPER_NO_CONTRACT; + _ASSERTE(!!m_array); + + return m_array->GetDirectPointerToNonObjectElements(); + } + + PTR_INT8 GetRaw() + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + _ASSERTE(!!m_array); + + return dac_cast(m_array->GetDirectPointerToNonObjectElements()); + } + + ArrayHeader const * GetHeader() const + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return dac_cast(GetRaw()); + } + + PTR_ArrayHeader GetHeader() + { + WRAPPER_NO_CONTRACT; + SUPPORTS_DAC; + return dac_cast(GetRaw()); + } + + void SetArray(I1ARRAYREF const & arr) + { + LIMITED_METHOD_CONTRACT; + m_array = arr; + } + +private: + // put only things here that can be protected with GCPROTECT + I1ARRAYREF m_array; + + friend struct ::cdac_data; +}; + +template<> +struct cdac_data +{ + static constexpr size_t Size = offsetof(StackTraceArray::ArrayHeader, m_size); + static constexpr size_t HeaderSize = sizeof(StackTraceArray::ArrayHeader); +}; + +#ifdef FEATURE_COLLECTIBLE_TYPES + +class LoaderAllocatorScoutObject : public Object +{ + friend class CoreLibBinder; + friend class LoaderAllocatorObject; + +protected: + LoaderAllocator * m_nativeLoaderAllocator; +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF LOADERALLOCATORSCOUTREF; +#else // USE_CHECKED_OBJECTREFS +typedef LoaderAllocatorScoutObject* LOADERALLOCATORSCOUTREF; +#endif // USE_CHECKED_OBJECTREFS + +class LoaderAllocatorObject : public Object +{ + friend class CoreLibBinder; + +public: + // All uses of this api must be safe lock-free reads used only for reading from the handle table + // The normal GetHandleTable can only be called while holding the handle table lock, but + // this is for use in lock-free scenarios + PTRARRAYREF DangerousGetHandleTable() + { + LIMITED_METHOD_DAC_CONTRACT; + return (PTRARRAYREF)ObjectToOBJECTREF(VolatileLoadWithoutBarrier((Object**)&m_pSlots)); + } + +#ifndef DACCESS_COMPILE + PTRARRAYREF GetHandleTable(); + void SetHandleTable(PTRARRAYREF handleTable); + INT32 GetSlotsUsed(); + void SetSlotsUsed(INT32 newSlotsUsed); +#endif // DACCESS_COMPILE + + void SetNativeLoaderAllocator(LoaderAllocator * pLoaderAllocator) + { + LIMITED_METHOD_CONTRACT; + m_pLoaderAllocatorScout->m_nativeLoaderAllocator = pLoaderAllocator; + } + + // README: + // If you modify the order of these fields, make sure to update the definition in + // BCL for this object. +protected: + LOADERALLOCATORSCOUTREF m_pLoaderAllocatorScout; + OBJECTREF m_pSlots; + INT32 m_slotsUsed; + OBJECTREF m_methodInstantiationsTable; +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF LOADERALLOCATORREF; +#else // USE_CHECKED_OBJECTREFS +typedef DPTR(LoaderAllocatorObject) PTR_LoaderAllocatorObject; +typedef PTR_LoaderAllocatorObject LOADERALLOCATORREF; +#endif // USE_CHECKED_OBJECTREFS + +#endif // FEATURE_COLLECTIBLE_TYPES + +typedef DPTR(class GenericCacheStruct) PTR_GenericCacheStruct; +class GenericCacheStruct +{ + friend class CoreLibBinder; + public: + + ARRAYBASEREF GetTable() const + { + LIMITED_METHOD_CONTRACT; + return _table; + } + + int32_t CacheElementCount() const + { + LIMITED_METHOD_CONTRACT; + return GetTable()->GetNumComponents() - 1; + } + + ARRAYBASEREF GetSentinelTable() const + { + LIMITED_METHOD_CONTRACT; + return _sentinelTable; + } + + void SetTable(ARRAYBASEREF table) + { + WRAPPER_NO_CONTRACT; + SetObjectReference((OBJECTREF*)&_table, (OBJECTREF)table); + } + + void SetLastFlushSize(int32_t lastFlushSize) + { + LIMITED_METHOD_CONTRACT; + _lastFlushSize = lastFlushSize; + } + + int32_t GetInitialCacheSize() const + { + LIMITED_METHOD_CONTRACT; + return _initialCacheSize; + } + +#ifdef DEBUG + static void ValidateLayout(MethodTable* pMTOfInstantiation); +#endif + + private: + // README: + // If you modify the order of these fields, make sure to update the definition in + // BCL for this object. + + ARRAYBASEREF _table; + ARRAYBASEREF _sentinelTable; + int32_t _lastFlushSize; + int32_t _initialCacheSize; + int32_t _maxCacheSize; +}; + +class ContinuationObject : public Object +{ + friend class CoreLibBinder; + + public: + CorInfoContinuationFlags GetFlags() const + { + LIMITED_METHOD_CONTRACT; + return (CorInfoContinuationFlags)Flags; + } + + void SetFlags(CorInfoContinuationFlags flags) + { + LIMITED_METHOD_CONTRACT; + Flags = (int32_t)flags; + } + + void SetResumeInfo(void* resumeInfo) + { + LIMITED_METHOD_CONTRACT; + ResumeInfo = resumeInfo; + } + + void* GetResumeInfo() const + { + LIMITED_METHOD_CONTRACT; + return ResumeInfo; + } + + void SetState(int32_t state) + { + LIMITED_METHOD_CONTRACT; + State = state; + } + + int32_t GetState() const + { + LIMITED_METHOD_CONTRACT; + return State; + } + + PTR_BYTE GetResultStorage() + { + LIMITED_METHOD_CONTRACT; + + uint32_t mask = (1u << CORINFO_CONTINUATION_RESULT_INDEX_NUM_BITS) - 1; + uint32_t index = ((uint32_t)Flags >> CORINFO_CONTINUATION_RESULT_INDEX_FIRST_BIT) & mask; + _ASSERTE(index != 0); + + uint32_t offset = OFFSETOF__CORINFO_Continuation__data + (index - 1) * TARGET_POINTER_SIZE; + PTR_BYTE dataAddress = dac_cast((dac_cast(this) + offset)); + return dataAddress; + } + + PTR_OBJECTREF GetExceptionObjectStorageOrNull() + { + LIMITED_METHOD_CONTRACT; + + uint32_t mask = (1u << CORINFO_CONTINUATION_EXCEPTION_INDEX_NUM_BITS) - 1; + uint32_t index = ((uint32_t)Flags >> CORINFO_CONTINUATION_EXCEPTION_INDEX_FIRST_BIT) & mask; + if (index == 0) + return NULL; + + uint32_t offset = OFFSETOF__CORINFO_Continuation__data + (index - 1) * TARGET_POINTER_SIZE; + PTR_BYTE dataAddress = dac_cast(dac_cast(this) + offset); + return dac_cast(dataAddress); + } + + PTR_OBJECTREF GetExecutionContextObjectStorageOrNull() + { + LIMITED_METHOD_CONTRACT; + + uint32_t mask = (1u << CORINFO_CONTINUATION_EXECUTION_CONTEXT_INDEX_NUM_BITS) - 1; + uint32_t index = ((uint32_t)Flags >> CORINFO_CONTINUATION_EXECUTION_CONTEXT_INDEX_FIRST_BIT) & mask; + if (index == 0) + return NULL; + + uint32_t offset = OFFSETOF__CORINFO_Continuation__data + (index - 1) * TARGET_POINTER_SIZE; + PTR_BYTE address = dac_cast(dac_cast(this) + offset); + return dac_cast(address); + } + + PTR_OBJECTREF GetContinuationContextObjectStorageOrNull() + { + LIMITED_METHOD_CONTRACT; + + uint32_t mask = (1u << CORINFO_CONTINUATION_CONTEXT_INDEX_NUM_BITS) - 1; + uint32_t index = ((uint32_t)Flags >> CORINFO_CONTINUATION_CONTEXT_INDEX_FIRST_BIT) & mask; + if (index == 0) + return NULL; + + uint32_t offset = OFFSETOF__CORINFO_Continuation__data + (index - 1) * TARGET_POINTER_SIZE; + PTR_BYTE address = dac_cast(dac_cast(this) + offset); + return dac_cast(address); + } + +#ifndef DACCESS_COMPILE + int32_t* GetFlagsAddress() + { + LIMITED_METHOD_CONTRACT; + return (int32_t*)&Flags; + } +#endif // DACCESS_COMPILE + +private: + // README: + // If you modify the order of these fields, make sure to update the definition in + // BCL for this object. + + CONTINUATIONREF Next; + void* ResumeInfo; + int32_t Flags; + int32_t State; +}; + +// This class corresponds to Exception on the managed side. +typedef DPTR(class ExceptionObject) PTR_ExceptionObject; +#include "pshpack4.h" +class ExceptionObject : public Object +{ + friend class CoreLibBinder; + +public: + void SetHResult(HRESULT hr) + { + LIMITED_METHOD_CONTRACT; + _HResult = hr; + } + + HRESULT GetHResult() + { + LIMITED_METHOD_CONTRACT; + return _HResult; + } + + void SetXCode(DWORD code) + { + LIMITED_METHOD_CONTRACT; + _xcode = code; + } + + DWORD GetXCode() + { + LIMITED_METHOD_CONTRACT; + return _xcode; + } + + void SetXPtrs(void* xptrs) + { + LIMITED_METHOD_CONTRACT; + _xptrs = xptrs; + } + + void* GetXPtrs() + { + LIMITED_METHOD_CONTRACT; + return _xptrs; + } + + void SetStackTrace(OBJECTREF stackTrace); + + void GetStackTrace(StackTraceArray & stackTrace, PTRARRAYREF * outKeepaliveArray = NULL) const + { +#ifdef DACCESS_COMPILE + return GetStackTrace(stackTrace, outKeepaliveArray, NULL); +#else + return GetStackTrace(stackTrace, outKeepaliveArray, GetThread()); +#endif // DACCESS_COMPILE + } + +private: + static void GetStackTraceClone(StackTraceArray & stackTrace, PTRARRAYREF * outKeepAliveArray); +public: + void GetStackTrace(StackTraceArray & stackTrace, PTRARRAYREF * outKeepaliveArray, Thread *pCurrentThread) const; + + static void GetStackTraceParts(OBJECTREF stackTraceObj, StackTraceArray & stackTrace, PTRARRAYREF * outKeepaliveArray); + + OBJECTREF GetStackTraceArrayObject() const + { + LIMITED_METHOD_DAC_CONTRACT; + return _stackTrace; + } + + void SetInnerException(OBJECTREF innerException) + { + WRAPPER_NO_CONTRACT; + SetObjectReference((OBJECTREF*)&_innerException, (OBJECTREF)innerException); + } + + OBJECTREF GetInnerException() + { + LIMITED_METHOD_DAC_CONTRACT; + return VolatileLoadWithoutBarrierOBJECTREF(&_innerException); + } + + // Returns the innermost exception object - equivalent of the + // managed System.Exception.GetBaseException method. + OBJECTREF GetBaseException() + { + LIMITED_METHOD_CONTRACT; + + // Loop and get the innermost exception object + OBJECTREF oInnerMostException = NULL; + OBJECTREF oCurrent = NULL; + + oCurrent = GetInnerException(); + while(oCurrent != NULL) + { + oInnerMostException = oCurrent; + oCurrent = ((ExceptionObject*)(Object *)OBJECTREFToObject(oCurrent))->GetInnerException(); + } + + // return the innermost exception + return oInnerMostException; + } + + void SetMessage(STRINGREF message) + { + WRAPPER_NO_CONTRACT; + SetObjectReference((OBJECTREF*)&_message, (OBJECTREF)message); + } + + STRINGREF GetMessage() + { + LIMITED_METHOD_DAC_CONTRACT; + return _message; + } + + void SetStackTraceString(STRINGREF stackTraceString) + { + WRAPPER_NO_CONTRACT; + SetObjectReference((OBJECTREF*)&_stackTraceString, (OBJECTREF)stackTraceString); + } + + STRINGREF GetStackTraceString() + { + LIMITED_METHOD_DAC_CONTRACT; + return _stackTraceString; + } + + STRINGREF GetRemoteStackTraceString() + { + LIMITED_METHOD_DAC_CONTRACT; + return (STRINGREF)VolatileLoadWithoutBarrierOBJECTREF(&_remoteStackTraceString); + } + + void SetHelpURL(STRINGREF helpURL) + { + WRAPPER_NO_CONTRACT; + SetObjectReference((OBJECTREF*)&_helpURL, (OBJECTREF)helpURL); + } + + void SetSource(STRINGREF source) + { + WRAPPER_NO_CONTRACT; + SetObjectReference((OBJECTREF*)&_source, (OBJECTREF)source); + } + + void ClearStackTraceForThrow() + { + WRAPPER_NO_CONTRACT; + SetObjectReference((OBJECTREF*)&_remoteStackTraceString, NULL); + SetObjectReference((OBJECTREF*)&_stackTrace, NULL); + SetObjectReference((OBJECTREF*)&_stackTraceString, NULL); + } + + void ClearStackTracePreservingRemoteStackTrace() + { + WRAPPER_NO_CONTRACT; + SetObjectReference((OBJECTREF*)&_stackTrace, NULL); + SetObjectReference((OBJECTREF*)&_stackTraceString, NULL); + } + + // This method will set the reference to the array + // containing the watson bucket information (in byte[] form). + void SetWatsonBucketReference(OBJECTREF oWatsonBucketArray) + { + WRAPPER_NO_CONTRACT; + SetObjectReference((OBJECTREF*)&_watsonBuckets, (OBJECTREF)oWatsonBucketArray); + } + + // This method will return the reference to the array + // containing the watson buckets + U1ARRAYREF GetWatsonBucketReference() + { + LIMITED_METHOD_CONTRACT; + return (U1ARRAYREF)VolatileLoadWithoutBarrierOBJECTREF(&_watsonBuckets); + } + + // This method will return a BOOL to indicate if the + // watson buckets are present or not. + BOOL AreWatsonBucketsPresent() + { + LIMITED_METHOD_CONTRACT; + return (GetWatsonBucketReference() != NULL)?TRUE:FALSE; + } + + // This method will save the IP to be used for watson bucketing. + void SetIPForWatsonBuckets(UINT_PTR ip) + { + LIMITED_METHOD_CONTRACT; + + _ipForWatsonBuckets = ip; + } + + // This method will return a BOOL to indicate if Watson bucketing IP + // is present (or not). + BOOL IsIPForWatsonBucketsPresent() + { + LIMITED_METHOD_CONTRACT; + + return (_ipForWatsonBuckets != 0); + } + + // This method returns the IP for Watson Buckets. + UINT_PTR GetIPForWatsonBuckets() + { + LIMITED_METHOD_CONTRACT; + + return VolatileLoadWithoutBarrier(&_ipForWatsonBuckets); + } + + // README: + // If you modify the order of these fields, make sure to update the definition in + // BCL for this object. +private: + OBJECTREF _exceptionMethod; //Needed for serialization. + STRINGREF _message; + OBJECTREF _data; + OBJECTREF _innerException; + STRINGREF _helpURL; + OBJECTREF _stackTrace; + U1ARRAYREF _watsonBuckets; + STRINGREF _stackTraceString; //Needed for serialization. + STRINGREF _remoteStackTraceString; + STRINGREF _source; // Mainly used by VB. + + UINT_PTR _ipForWatsonBuckets; // Contains the IP of exception for watson bucketing + void* _xptrs; + INT32 _xcode; + INT32 _HResult; + + friend struct ::cdac_data; +}; + +template<> +struct cdac_data +{ + static constexpr size_t _message = offsetof(ExceptionObject, _message); + static constexpr size_t _innerException = offsetof(ExceptionObject, _innerException); + static constexpr size_t _stackTrace = offsetof(ExceptionObject, _stackTrace); + static constexpr size_t _watsonBuckets = offsetof(ExceptionObject, _watsonBuckets); + static constexpr size_t _stackTraceString = offsetof(ExceptionObject, _stackTraceString); + static constexpr size_t _remoteStackTraceString = offsetof(ExceptionObject, _remoteStackTraceString); + static constexpr size_t _HResult = offsetof(ExceptionObject, _HResult); + static constexpr size_t _xcode = offsetof(ExceptionObject, _xcode); +}; + +#include "poppack.h" + +//=============================================================================== +// #NullableFeature +// #NullableArchitecture +// +// In a nutshell it is counterintuitive to have a boxed Nullable, since a boxed +// object already has a representation for null (the null pointer), and having +// multiple representations for the 'not present' value just causes grief. Thus the +// feature is build make Nullable box to a boxed (not boxed). +// +// We want to do this in a way that does not impact the perf of the runtime in the +// non-nullable case. +// +// To do this we need to +// * Make a new helper for the Unbox case (see code:JIT_Unbox_Nullable) +// * Plumb the JIT to ask for what kind of Boxing helper is needed +// (see code:CEEInfo.getBoxHelper, code:CEEInfo.getUnBoxHelper +// * change all the places in the CLR where we box or unbox by hand, and force +// them to use code:MethodTable.Box, and code:MethodTable.Unbox which in +// turn call code:Nullable.Box and code:Nullable.UnBox, most of these +// are in reflection, and remoting (passing and returning value types). +// +// #NullableVerification +// +// Sadly, the IL Verifier also needs to know about this change. Basically the 'box' +// instruction returns a boxed(T) (not a boxed(Nullable)) for the purposes of +// verfication. The JIT finds out what box returns by calling back to the EE with +// the code:CEEInfo.getTypeForBox API. +// +// #NullableDebugging +// +// Sadly, because the debugger also does its own boxing 'by hand' for expression +// evaluation inside visual studio, it measn that debuggers also need to be aware +// of the fact that Nullable boxes to a boxed. It is the responcibility of +// debuggers to follow this convention (which is why this is sad). +// + +//=============================================================================== +// Nullable represents the managed generic value type Nullable +// +// The runtime has special logic for this value class. When it is boxed +// it becomes either null or a boxed T. Similarly a boxed T can be unboxed +// either as a T (as normal), or as a Nullable +// +// See code:Nullable#NullableArchitecture for more. +// +class Nullable { + Nullable(); // This is purposefully undefined. Do not make instances + // of this class. +public: + static void CheckFieldOffsets(TypeHandle nullableType); + static BOOL IsNullableType(TypeHandle nullableType); + static BOOL IsNullableForType(TypeHandle nullableType, MethodTable* paramMT); + + static OBJECTREF Box(void* src, MethodTable* nullable); + static BOOL UnBox(void* dest, OBJECTREF boxedVal, MethodTable* destMT); + static void UnBoxNoCheck(void* dest, OBJECTREF boxedVal, MethodTable* destMT); + static OBJECTREF BoxedNullableNull(TypeHandle nullableType) { return NULL; } + // if 'Obj' is a true boxed nullable, return the form we want (either null or a boxed T) + static OBJECTREF NormalizeBox(OBJECTREF obj); + + static inline CLR_BOOL HasValue(void *src, MethodTable *nullableMT) + { + Nullable *nullable = (Nullable *)src; + return *(nullable->HasValueAddr(nullableMT)); + } + + static inline void *Value(void *src, MethodTable *nullableMT) + { + Nullable *nullable = (Nullable *)src; + return nullable->ValueAddr(nullableMT); + } + + static int32_t GetValueAddrOffset(MethodTable* nullableMT); + +private: + static BOOL IsNullableForTypeHelper(MethodTable* nullableMT, MethodTable* paramMT); + + CLR_BOOL* HasValueAddr(MethodTable* nullableMT); + void* ValueAddr(MethodTable* nullableMT); +}; + +#ifdef USE_CHECKED_OBJECTREFS +typedef REF EXCEPTIONREF; +#else // USE_CHECKED_OBJECTREFS +typedef PTR_ExceptionObject EXCEPTIONREF; +#endif // USE_CHECKED_OBJECTREFS + +#endif // _OBJECT_H_ diff --git a/src/coreclr/vm/riscv64/stubs.cpp b/src/coreclr/vm/riscv64/stubs.cpp index 9064d3f844f5fd..9235b6b7e46cff 100644 --- a/src/coreclr/vm/riscv64/stubs.cpp +++ b/src/coreclr/vm/riscv64/stubs.cpp @@ -887,9 +887,9 @@ VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) static const IntReg t6 = 31, t5 = 30, a0 = argRegBase + 0; // On entry a0 holds the delegate instance. Look up the real target address stored in the MethodPtrAux // field and saved in t6. Tailcall to the target method after re-arranging the arguments - EmitLoad(t6, a0, DelegateObject::GetOffsetOfMethodPtrAux()); + EmitLoad(t6, a0, DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); // load the indirection cell into t5 used by ResolveWorkerAsmStub - EmitAddImm(t5, a0, DelegateObject::GetOffsetOfMethodPtrAux()); + EmitAddImm(t5, a0, DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); const ShuffleEntry* entry = pShuffleEntryArray; // Shuffle integer argument registers diff --git a/src/coreclr/vm/stubhelpers.cpp b/src/coreclr/vm/stubhelpers.cpp index 7e64050dfac66a..298885004e5cd5 100644 --- a/src/coreclr/vm/stubhelpers.cpp +++ b/src/coreclr/vm/stubhelpers.cpp @@ -511,14 +511,14 @@ FCIMPL1(void*, StubHelpers::GetDelegateTarget, DelegateObject *pThisUNSAFE) DELEGATEREF orefThis = (DELEGATEREF)ObjectToOBJECTREF(pThisUNSAFE); #if defined(HOST_64BIT) - UINT_PTR target = (UINT_PTR)orefThis->GetMethodPtrAux(); + UINT_PTR target = (UINT_PTR)orefThis->GetExtraFunctionPointerOrData(); // See code:GenericPInvokeCalliHelper // The lowest bit is used to distinguish between MD and target on 64-bit. target = (target << 1) | 1; #endif // HOST_64BIT - pEntryPoint = orefThis->GetMethodPtrAux(); + pEntryPoint = orefThis->GetExtraFunctionPointerOrData(); return (PVOID)pEntryPoint; } diff --git a/src/coreclr/vm/stubmgr.cpp b/src/coreclr/vm/stubmgr.cpp index e8f55803ece74a..6bd5424250a18d 100644 --- a/src/coreclr/vm/stubmgr.cpp +++ b/src/coreclr/vm/stubmgr.cpp @@ -1208,7 +1208,7 @@ BOOL StubLinkStubManager::TraceDelegateObject(BYTE* pbDel, TraceDestination *tra // If we got here, then we're here b/c we're at the start of a delegate stub // need to figure out the kind of delegates we are dealing with. - BYTE *pbDelInvocationList = *(BYTE **)(pbDel + DelegateObject::GetOffsetOfInvocationList()); + BYTE *pbDelInvocationList = *(BYTE **)(pbDel + DelegateObject::GetOffsetOfHelperObject()); LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: invocationList: %p\n", pbDelInvocationList)); @@ -1220,10 +1220,10 @@ BOOL StubLinkStubManager::TraceDelegateObject(BYTE* pbDel, TraceDestination *tra // For the others the logic is the following: // if _methodPtrAux is 0 the target is in _methodPtr, otherwise the taret is _methodPtrAux - ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtrAux()); + ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); if (*ppbDest == NULL) { - ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtr()); + ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfFunctionPointer()); if (*ppbDest == NULL) { @@ -1250,7 +1250,7 @@ BOOL StubLinkStubManager::TraceDelegateObject(BYTE* pbDel, TraceDestination *tra if (pbCount == NULL) { // it's a static closed, the target lives in _methodAuxPtr - ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtrAux()); + ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); if (*ppbDest == NULL) { @@ -1374,7 +1374,7 @@ static BOOL TraceShuffleThunk( *pRetAddr = (BYTE *)StubManagerHelpers::GetReturnAddress(pContext); DELEGATEREF orDelegate = (DELEGATEREF)ObjectToOBJECTREF(StubManagerHelpers::GetThisPtr(pContext)); - PCODE destAddr = orDelegate->GetMethodPtrAux(); + PCODE destAddr = orDelegate->GetExtraFunctionPointerOrData(); LOG((LF_CORDB,LL_INFO10000, "TraceShuffleThunk: ppbDest: %p\n", destAddr)); BOOL res = StubManager::TraceStub(destAddr, trace); @@ -1763,7 +1763,7 @@ BOOL ILStubManager::TraceManager(Thread *thread, { // This is forward delegate P/Invoke stub, the argument is undefined DelegateObject *pDel = (DelegateObject *)pThis; - target = pDel->GetMethodPtrAux(); + target = pDel->GetExtraFunctionPointerOrData(); LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: Forward delegate P/Invoke case %p\n", target)); trace->InitForUnmanaged(target); diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index dd256a50499126..de07be5c0a4a34 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -1446,7 +1446,7 @@ extern "C" PCODE CID_VirtualOpenDelegateDispatchWorker(TransitionBlock * pTransi MODE_COOPERATIVE; } CONTRACTL_END; - OBJECTREF delegateObj = ObjectToOBJECTREF((Object*)(((BYTE*)ppMethodPtrAuxField) - DelegateObject::GetOffsetOfMethodPtrAux())); + OBJECTREF delegateObj = ObjectToOBJECTREF((Object*)(((BYTE*)ppMethodPtrAuxField) - DelegateObject::GetOffsetOfExtraFunctionPointerOrData())); MAKE_CURRENT_THREAD_AVAILABLE(); #ifdef _DEBUG diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs index 154cd64a421e88..99a7bc2abbf0b5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs @@ -72,6 +72,15 @@ public static int SizeOf() // ret } + // Internal helper that asserts the As is valid in Debug and Checked + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [return: NotNullIfNotNull(nameof(o))] + internal static T? AsAssert(object? o) where T : class? + { + Debug.Assert(o is null or T); + return As(o); + } + /// /// Reinterprets the given reference as a reference to a value of type . /// From 29a5ad4da784101975c6f956dd2467b2072e475e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Wed, 17 Jun 2026 03:11:14 +0200 Subject: [PATCH 29/77] Remove junk --- .../src/System/Delegate.CoreCLR.cs.bak | 592 ---- .../src/System/Delegate.CoreCLR.cs.orig | 592 ---- src/coreclr/vm/object.h.bak | 2586 ----------------- src/coreclr/vm/object.h.orig | 2586 ----------------- 4 files changed, 6356 deletions(-) delete mode 100644 src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs.bak delete mode 100644 src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs.orig delete mode 100644 src/coreclr/vm/object.h.bak delete mode 100644 src/coreclr/vm/object.h.orig diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs.bak b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs.bak deleted file mode 100644 index 5319d8a8d2f6a0..00000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs.bak +++ /dev/null @@ -1,592 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Serialization; - -namespace System -{ - [ClassInterface(ClassInterfaceType.None)] - [ComVisible(true)] - public abstract partial class Delegate : ICloneable, ISerializable - { -<<<<<<< HEAD - // _target is the object we will invoke on - internal object? _target; // Initialized by VM as needed; null if static delegate - - // VM handle to wrapped method, null when not initialized - private IntPtr _methodDesc; -======= - // MethodBase, either cached after first request or assigned from a DynamicMethod - // For open delegates to collectible types, this may be a LoaderAllocator object - internal object? _methodBase; - - // _target is the object we will invoke on; null if static delegate - internal object? _target; // Keep _target and _methodPtr next to each other for optimal delegate invoke performance ->>>>>>> upstream/main - - // _methodPtr is a pointer to the method we will invoke - // It could be a small thunk if this is a static or UM call - internal IntPtr _methodPtr; - - // In the case of a static method passed to a delegate, this field stores - // whatever _methodPtr would have stored: and _methodPtr points to a - // small thunk which removes the "this" pointer before going on - // to _methodPtrAux. - internal IntPtr _methodPtrAux; - - internal IntPtr MethodDesc - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return _methodDesc != 0 ? _methodDesc : GetMethodDesc(); - } - } - - // This constructor is called from the class generated by the - // compiler generated code - [RequiresUnreferencedCode("The target method might be removed")] - protected Delegate(object target, string method) - { - ArgumentNullException.ThrowIfNull(target); - ArgumentNullException.ThrowIfNull(method); - - // This API existed in v1/v1.1 and only expected to create closed - // instance delegates. Constrain the call to BindToMethodName to - // such and don't allow relaxed signature matching (which could make - // the choice of target method ambiguous) for backwards - // compatibility. The name matching was case sensitive and we - // preserve that as well. - if (!BindToMethodName(target, (RuntimeType)target.GetType(), method, - DelegateBindingFlags.InstanceMethodOnly | - DelegateBindingFlags.ClosedDelegateOnly)) - throw new ArgumentException(SR.Arg_DlgtTargMeth); - } - - // This constructor is called from a class to generate a - // delegate based upon a static method name and the Type object - // for the class defining the method. - protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllMethods)] Type target, string method) - { - ArgumentNullException.ThrowIfNull(target); - ArgumentNullException.ThrowIfNull(method); - - if (target.ContainsGenericParameters) - throw new ArgumentException(SR.Arg_UnboundGenParam, nameof(target)); - if (target is not RuntimeType rtTarget) - throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(target)); - - // This API existed in v1/v1.1 and only expected to create open - // static delegates. Constrain the call to BindToMethodName to such - // and don't allow relaxed signature matching (which could make the - // choice of target method ambiguous) for backwards compatibility. - // The name matching was case insensitive (no idea why this is - // different from the constructor above) and we preserve that as - // well. - BindToMethodName(null, rtTarget, method, - DelegateBindingFlags.StaticMethodOnly | - DelegateBindingFlags.OpenDelegateOnly | - DelegateBindingFlags.CaselessMatching); - } - - protected virtual object? DynamicInvokeImpl(object?[]? args) - { - RuntimeMethodHandleInternal method = new RuntimeMethodHandleInternal(GetInvokeMethod()); - RuntimeMethodInfo invoke = (RuntimeMethodInfo)RuntimeType.GetMethodBase((RuntimeType)this.GetType(), method)!; - - return invoke.Invoke(this, BindingFlags.Default, null, args, null); - } - - public override bool Equals([NotNullWhen(true)] object? obj) - { - if (obj == null || !InternalEqualTypes(this, obj)) - return false; - - Delegate d = (Delegate)obj; - - // do an optimistic check first. This is hopefully cheap enough to be worth - if (_target == d._target && _methodPtr == d._methodPtr && _methodPtrAux == d._methodPtrAux) - return true; - - // even though the fields were not all equals the delegates may still match - // When target carries the delegate itself the 2 targets (delegates) may be different instances - // but the delegates are logically the same - // It may also happen that the method pointer was not jitted when creating one delegate and jitted in the other - // if that's the case the delegates may still be equals but we need to make a more complicated check - - if (_methodPtrAux == IntPtr.Zero) - { - if (d._methodPtrAux != IntPtr.Zero) - return false; // different delegate kind - - // they are both closed over the first arg - if (_target != d._target) - return false; - - // fall through method handle check - } - else - { - if (d._methodPtrAux == IntPtr.Zero) - return false; // different delegate kind - - // Ignore the target as it will be the delegate instance, though it may be a different one - /* - if (_methodPtr != d._methodPtr) - return false; - */ - - if (_methodPtrAux == d._methodPtrAux) - return true; - - // fall through method handle check - } - - return MethodDesc == d.MethodDesc; - } - - public override int GetHashCode() - { - int hashCode = MethodDesc.GetHashCode(); - if (_methodPtrAux == IntPtr.Zero && _target != null) - { - hashCode += RuntimeHelpers.GetHashCode(_target) * 33; - } - return hashCode; - } - - protected virtual MethodInfo GetMethodImpl() - { - return Cache.s_methodCache.TryGetValue(this, out MethodInfo? cachedValue) ? cachedValue : GetMethodImplUncached(); - - [MethodImpl(MethodImplOptions.NoInlining)] - MethodInfo GetMethodImplUncached() - { - Debug.Assert(this is MulticastDelegate); - MethodInfo method = Unsafe.As(this).GetMethodImplMulticast(); - - Debug.Assert(method is not null); - Cache.s_methodCache.AddOrUpdate(this, method); - return method; - } - } - - internal MethodInfo GetMethodImplSimple() - { - IRuntimeMethodInfo method = CreateMethodInfo(MethodDesc); - RuntimeType? declaringType = RuntimeMethodHandle.GetDeclaringType(method); - - // need a proper declaring type instance method on a generic type - if (declaringType.IsGenericType) - { - bool isStatic = (RuntimeMethodHandle.GetAttributes(method) & MethodAttributes.Static) != (MethodAttributes)0; - if (!isStatic) - { - if (_methodPtrAux == IntPtr.Zero) - { - // The target may be of a derived type that doesn't have visibility onto the - // target method. We don't want to call RuntimeType.GetMethodBase below with that - // or reflection can end up generating a MethodInfo where the ReflectedType cannot - // see the MethodInfo itself and that breaks an important invariant. But the - // target type could include important generic type information we need in order - // to work out what the exact instantiation of the method's declaring type is. So - // we'll walk up the inheritance chain (which will yield exactly instantiated - // types at each step) until we find the declaring type. Since the declaring type - // we get from the method is probably shared and those in the hierarchy we're - // walking won't be we compare using the generic type definition forms instead. - Type targetType = declaringType.GetGenericTypeDefinition(); - Type? currentType; - for (currentType = _target!.GetType(); currentType != null; currentType = currentType.BaseType) - { - if (currentType.IsGenericType && - currentType.GetGenericTypeDefinition() == targetType) - { - declaringType = currentType as RuntimeType; - break; - } - } - - // RCWs don't need to be "strongly-typed" in which case we don't find a base type - // that matches the declaring type of the method. This is fine because interop needs - // to work with exact methods anyway so declaringType is never shared at this point. - // The targetType may also be an interface with a Default interface method (DIM). - Debug.Assert( - currentType != null - || _target.GetType().IsCOMObject - || targetType.IsInterface, "The class hierarchy should declare the method or be a DIM"); - } - else - { - // it's an open one, need to fetch the first arg of the instantiation - MethodInfo invoke = this.GetType().GetMethod("Invoke")!; - declaringType = (RuntimeType)invoke.GetParametersAsSpan()[0].ParameterType; - } - } - } - return (MethodInfo)RuntimeType.GetMethodBase(declaringType, method)!; - } - - public object? Target => GetTarget(); - - // V1 API. - [RequiresUnreferencedCode("The target method might be removed")] - public static Delegate? CreateDelegate(Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure) - { - ArgumentNullException.ThrowIfNull(type); - ArgumentNullException.ThrowIfNull(target); - ArgumentNullException.ThrowIfNull(method); - - if (type is not RuntimeType rtType) - throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); - if (!rtType.IsDelegate()) - throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); - - Delegate d = InternalAlloc(rtType); - // This API existed in v1/v1.1 and only expected to create closed - // instance delegates. Constrain the call to BindToMethodName to such - // and don't allow relaxed signature matching (which could make the - // choice of target method ambiguous) for backwards compatibility. - // We never generate a closed over null delegate and this is - // actually enforced via the check on target above, but we pass - // NeverCloseOverNull anyway just for clarity. - if (!d.BindToMethodName(target, (RuntimeType)target.GetType(), method, - DelegateBindingFlags.InstanceMethodOnly | - DelegateBindingFlags.ClosedDelegateOnly | - DelegateBindingFlags.NeverCloseOverNull | - (ignoreCase ? DelegateBindingFlags.CaselessMatching : 0))) - { - if (throwOnBindFailure) - throw new ArgumentException(SR.Arg_DlgtTargMeth); - - return null; - } - - return d; - } - - // V1 API. - public static Delegate? CreateDelegate(Type type, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllMethods)] Type target, string method, bool ignoreCase, bool throwOnBindFailure) - { - ArgumentNullException.ThrowIfNull(type); - ArgumentNullException.ThrowIfNull(target); - ArgumentNullException.ThrowIfNull(method); - - if (target.ContainsGenericParameters) - throw new ArgumentException(SR.Arg_UnboundGenParam, nameof(target)); - if (type is not RuntimeType rtType) - throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); - if (target is not RuntimeType rtTarget) - throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(target)); - - if (!rtType.IsDelegate()) - throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); - - Delegate d = InternalAlloc(rtType); - // This API existed in v1/v1.1 and only expected to create open - // static delegates. Constrain the call to BindToMethodName to such - // and don't allow relaxed signature matching (which could make the - // choice of target method ambiguous) for backwards compatibility. - if (!d.BindToMethodName(null, rtTarget, method, - DelegateBindingFlags.StaticMethodOnly | - DelegateBindingFlags.OpenDelegateOnly | - (ignoreCase ? DelegateBindingFlags.CaselessMatching : 0))) - { - if (throwOnBindFailure) - throw new ArgumentException(SR.Arg_DlgtTargMeth); - - return null; - } - - return d; - } - - // V1 API. - public static Delegate? CreateDelegate(Type type, MethodInfo method, bool throwOnBindFailure) - { - ArgumentNullException.ThrowIfNull(type); - ArgumentNullException.ThrowIfNull(method); - - if (type is not RuntimeType rtType) - throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); - - if (method is not RuntimeMethodInfo rmi) - throw new ArgumentException(SR.Argument_MustBeRuntimeMethodInfo, nameof(method)); - - if (!rtType.IsDelegate()) - throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); - - // This API existed in v1/v1.1 and only expected to create closed - // instance delegates. Constrain the call to BindToMethodInfo to - // open delegates only for backwards compatibility. But we'll allow - // relaxed signature checking and open static delegates because - // there's no ambiguity there (the caller would have to explicitly - // pass us a static method or a method with a non-exact signature - // and the only change in behavior from v1.1 there is that we won't - // fail the call). - Delegate? d = CreateDelegateInternal( - rtType, - rmi, - null, - DelegateBindingFlags.OpenDelegateOnly | DelegateBindingFlags.RelaxedSignature); - - if (d == null && throwOnBindFailure) - throw new ArgumentException(SR.Arg_DlgtTargMeth); - - return d; - } - - // V2 API. - public static Delegate? CreateDelegate(Type type, object? firstArgument, MethodInfo method, bool throwOnBindFailure) - { - ArgumentNullException.ThrowIfNull(type); - ArgumentNullException.ThrowIfNull(method); - - if (type is not RuntimeType rtType) - throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); - - if (method is not RuntimeMethodInfo rmi) - throw new ArgumentException(SR.Argument_MustBeRuntimeMethodInfo, nameof(method)); - - if (!rtType.IsDelegate()) - throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); - - // This API is new in Whidbey and allows the full range of delegate - // flexability (open or closed delegates binding to static or - // instance methods with relaxed signature checking. The delegate - // can also be closed over null. There's no ambiguity with all these - // options since the caller is providing us a specific MethodInfo. - Delegate? d = CreateDelegateInternal( - rtType, - rmi, - firstArgument, - DelegateBindingFlags.RelaxedSignature); - - if (d == null && throwOnBindFailure) - throw new ArgumentException(SR.Arg_DlgtTargMeth); - - return d; - } - - // - // internal implementation details (FCALLS and utilities) - // - - // V2 internal API. - internal static Delegate CreateDelegateNoSecurityCheck(Type type, object? target, RuntimeMethodHandle method) - { - ArgumentNullException.ThrowIfNull(type); - - if (method.IsNullHandle()) - throw new ArgumentNullException(nameof(method)); - - if (type is not RuntimeType rtType) - throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); - - if (!rtType.IsDelegate()) - throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); - - // Initialize the method... - Delegate d = InternalAlloc(rtType); - // This is a new internal API added in Whidbey. Currently it's only - // used by the dynamic method code to generate a wrapper delegate. - // Allow flexible binding options since the target method is - // unambiguously provided to us. - - if (!d.BindToMethodInfo(target, - method.GetMethodInfo(), - RuntimeMethodHandle.GetDeclaringType(method.GetMethodInfo()), - DelegateBindingFlags.RelaxedSignature)) - throw new ArgumentException(SR.Arg_DlgtTargMeth); - return d; - } - - internal static Delegate? CreateDelegateInternal(RuntimeType rtType, RuntimeMethodInfo rtMethod, object? firstArgument, DelegateBindingFlags flags) - { - Delegate d = InternalAlloc(rtType); - - if (d.BindToMethodInfo(firstArgument, rtMethod, rtMethod.GetDeclaringTypeInternal(), flags)) - return d; - else - return null; - } - - // - // internal implementation details (FCALLS and utilities) - // - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2067:ParameterDoesntMeetParameterRequirements", - Justification = "The parameter 'methodType' is passed by ref to QCallTypeHandle")] - private bool BindToMethodName(object? target, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllMethods)] RuntimeType methodType, string method, DelegateBindingFlags flags) - { - Delegate d = this; - return BindToMethodName(ObjectHandleOnStack.Create(ref d), ObjectHandleOnStack.Create(ref target), - new QCallTypeHandle(ref methodType), method, flags); - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_BindToMethodName", StringMarshalling = StringMarshalling.Utf8)] - [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool BindToMethodName(ObjectHandleOnStack d, ObjectHandleOnStack target, QCallTypeHandle methodType, string method, DelegateBindingFlags flags); - - private bool BindToMethodInfo(object? target, IRuntimeMethodInfo method, RuntimeType methodType, DelegateBindingFlags flags) - { - Delegate d = this; - bool ret = BindToMethodInfo(ObjectHandleOnStack.Create(ref d), ObjectHandleOnStack.Create(ref target), - method.Value, new QCallTypeHandle(ref methodType), flags); - GC.KeepAlive(method); - return ret; - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_BindToMethodInfo")] - [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool BindToMethodInfo(ObjectHandleOnStack d, ObjectHandleOnStack target, RuntimeMethodHandleInternal method, QCallTypeHandle methodType, DelegateBindingFlags flags); - - private static MulticastDelegate InternalAlloc(RuntimeType type) - { - Debug.Assert(type.IsAssignableTo(typeof(MulticastDelegate))); - return Unsafe.As(RuntimeTypeHandle.InternalAlloc(type)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static unsafe bool InternalEqualTypes(object a, object b) - { - if (a.GetType() == b.GetType()) - return true; -#if FEATURE_TYPEEQUIVALENCE - MethodTable* pMTa = RuntimeHelpers.GetMethodTable(a); - MethodTable* pMTb = RuntimeHelpers.GetMethodTable(b); - - bool ret; - - // only use QCall to check the type equivalence scenario - if (pMTa->HasTypeEquivalence && pMTb->HasTypeEquivalence) - ret = RuntimeHelpers.AreTypesEquivalent(pMTa, pMTb); - else - ret = false; - - GC.KeepAlive(a); - GC.KeepAlive(b); - - return ret; -#else - return false; -#endif // FEATURE_TYPEEQUIVALENCE - } - - // Used by the ctor. Do not call directly. - // The name of this function will appear in managed stacktraces as delegate constructor. - private void DelegateConstruct(object target, IntPtr method) - { - // Via reflection you can pass in just about any value for the method. - // We can do some basic verification up front to prevent EE exceptions. - if (method == IntPtr.Zero) - { - throw new ArgumentNullException(nameof(method)); - } - - Delegate _this = this; - Construct(ObjectHandleOnStack.Create(ref _this), ObjectHandleOnStack.Create(ref target), method); - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_Construct")] - private static partial void Construct(ObjectHandleOnStack _this, ObjectHandleOnStack target, IntPtr method); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe void* GetMulticastInvoke(MethodTable* pMT); - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_GetMulticastInvokeSlow")] - private static unsafe partial void* GetMulticastInvokeSlow(MethodTable* pMT); - - internal unsafe IntPtr GetMulticastInvoke() - { - MethodTable* pMT = RuntimeHelpers.GetMethodTable(this); - void* ptr = GetMulticastInvoke(pMT); - if (ptr == null) - { - ptr = GetMulticastInvokeSlow(pMT); - Debug.Assert(ptr != null); - Debug.Assert(ptr == GetMulticastInvoke(pMT)); - } - // No GC.KeepAlive() since the caller must keep instance alive to use returned pointer. - return (IntPtr)ptr; - } - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe void* GetInvokeMethod(MethodTable* pMT); - - internal unsafe IntPtr GetInvokeMethod() - { - MethodTable* pMT = RuntimeHelpers.GetMethodTable(this); - void* ptr = GetInvokeMethod(pMT); - // No GC.KeepAlive() since the caller must keep instance alive to use returned pointer. - return (IntPtr)ptr; - } - - internal static IRuntimeMethodInfo CreateMethodInfo(IntPtr methodDesc) - { - IRuntimeMethodInfo? methodInfo = null; - CreateMethodInfo(methodDesc, ObjectHandleOnStack.Create(ref methodInfo)); - return methodInfo!; - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_CreateMethodInfo")] - private static partial void CreateMethodInfo(IntPtr methodDesc, ObjectHandleOnStack retMethodInfo); - - [MethodImpl(MethodImplOptions.NoInlining)] - private IntPtr GetMethodDesc() - { - Delegate d = this; - IntPtr desc = GetMethodDesc(ObjectHandleOnStack.Create(ref d)); - _methodDesc = desc; - return desc; - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_GetMethodDesc")] - private static partial IntPtr GetMethodDesc(ObjectHandleOnStack instance); - - internal static IntPtr AdjustTarget(object target, IntPtr methodPtr) - { - return AdjustTarget(ObjectHandleOnStack.Create(ref target), methodPtr); - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_AdjustTarget")] - private static partial IntPtr AdjustTarget(ObjectHandleOnStack target, IntPtr methodPtr); - - internal void InitializeVirtualCallStub(IntPtr methodPtr) - { - Delegate d = this; - InitializeVirtualCallStub(ObjectHandleOnStack.Create(ref d), methodPtr); - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_InitializeVirtualCallStub")] - private static partial void InitializeVirtualCallStub(ObjectHandleOnStack d, IntPtr methodPtr); - - internal virtual object? GetTarget() - { - return (_methodPtrAux == IntPtr.Zero) ? _target : null; - } - - // Caches MethodInfos, added either after first request or assigned from a DynamicMethod - private static class Cache - { - public static readonly ConditionalWeakTable s_methodCache = new(); - } - } - - // These flags effect the way BindToMethodInfo and BindToMethodName are allowed to bind a delegate to a target method. Their - // values must be kept in sync with the definition in vm\comdelegate.h. - internal enum DelegateBindingFlags - { - StaticMethodOnly = 0x00000001, // Can only bind to static target methods - InstanceMethodOnly = 0x00000002, // Can only bind to instance (including virtual) methods - OpenDelegateOnly = 0x00000004, // Only allow the creation of delegates open over the 1st argument - ClosedDelegateOnly = 0x00000008, // Only allow the creation of delegates closed over the 1st argument - NeverCloseOverNull = 0x00000010, // A null target will never been considered as a possible null 1st argument - CaselessMatching = 0x00000020, // Use case insensitive lookup for methods matched by name - RelaxedSignature = 0x00000040, // Allow relaxed signature matching (co/contra variance) - } -} diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs.orig b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs.orig deleted file mode 100644 index 5319d8a8d2f6a0..00000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs.orig +++ /dev/null @@ -1,592 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Serialization; - -namespace System -{ - [ClassInterface(ClassInterfaceType.None)] - [ComVisible(true)] - public abstract partial class Delegate : ICloneable, ISerializable - { -<<<<<<< HEAD - // _target is the object we will invoke on - internal object? _target; // Initialized by VM as needed; null if static delegate - - // VM handle to wrapped method, null when not initialized - private IntPtr _methodDesc; -======= - // MethodBase, either cached after first request or assigned from a DynamicMethod - // For open delegates to collectible types, this may be a LoaderAllocator object - internal object? _methodBase; - - // _target is the object we will invoke on; null if static delegate - internal object? _target; // Keep _target and _methodPtr next to each other for optimal delegate invoke performance ->>>>>>> upstream/main - - // _methodPtr is a pointer to the method we will invoke - // It could be a small thunk if this is a static or UM call - internal IntPtr _methodPtr; - - // In the case of a static method passed to a delegate, this field stores - // whatever _methodPtr would have stored: and _methodPtr points to a - // small thunk which removes the "this" pointer before going on - // to _methodPtrAux. - internal IntPtr _methodPtrAux; - - internal IntPtr MethodDesc - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - return _methodDesc != 0 ? _methodDesc : GetMethodDesc(); - } - } - - // This constructor is called from the class generated by the - // compiler generated code - [RequiresUnreferencedCode("The target method might be removed")] - protected Delegate(object target, string method) - { - ArgumentNullException.ThrowIfNull(target); - ArgumentNullException.ThrowIfNull(method); - - // This API existed in v1/v1.1 and only expected to create closed - // instance delegates. Constrain the call to BindToMethodName to - // such and don't allow relaxed signature matching (which could make - // the choice of target method ambiguous) for backwards - // compatibility. The name matching was case sensitive and we - // preserve that as well. - if (!BindToMethodName(target, (RuntimeType)target.GetType(), method, - DelegateBindingFlags.InstanceMethodOnly | - DelegateBindingFlags.ClosedDelegateOnly)) - throw new ArgumentException(SR.Arg_DlgtTargMeth); - } - - // This constructor is called from a class to generate a - // delegate based upon a static method name and the Type object - // for the class defining the method. - protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllMethods)] Type target, string method) - { - ArgumentNullException.ThrowIfNull(target); - ArgumentNullException.ThrowIfNull(method); - - if (target.ContainsGenericParameters) - throw new ArgumentException(SR.Arg_UnboundGenParam, nameof(target)); - if (target is not RuntimeType rtTarget) - throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(target)); - - // This API existed in v1/v1.1 and only expected to create open - // static delegates. Constrain the call to BindToMethodName to such - // and don't allow relaxed signature matching (which could make the - // choice of target method ambiguous) for backwards compatibility. - // The name matching was case insensitive (no idea why this is - // different from the constructor above) and we preserve that as - // well. - BindToMethodName(null, rtTarget, method, - DelegateBindingFlags.StaticMethodOnly | - DelegateBindingFlags.OpenDelegateOnly | - DelegateBindingFlags.CaselessMatching); - } - - protected virtual object? DynamicInvokeImpl(object?[]? args) - { - RuntimeMethodHandleInternal method = new RuntimeMethodHandleInternal(GetInvokeMethod()); - RuntimeMethodInfo invoke = (RuntimeMethodInfo)RuntimeType.GetMethodBase((RuntimeType)this.GetType(), method)!; - - return invoke.Invoke(this, BindingFlags.Default, null, args, null); - } - - public override bool Equals([NotNullWhen(true)] object? obj) - { - if (obj == null || !InternalEqualTypes(this, obj)) - return false; - - Delegate d = (Delegate)obj; - - // do an optimistic check first. This is hopefully cheap enough to be worth - if (_target == d._target && _methodPtr == d._methodPtr && _methodPtrAux == d._methodPtrAux) - return true; - - // even though the fields were not all equals the delegates may still match - // When target carries the delegate itself the 2 targets (delegates) may be different instances - // but the delegates are logically the same - // It may also happen that the method pointer was not jitted when creating one delegate and jitted in the other - // if that's the case the delegates may still be equals but we need to make a more complicated check - - if (_methodPtrAux == IntPtr.Zero) - { - if (d._methodPtrAux != IntPtr.Zero) - return false; // different delegate kind - - // they are both closed over the first arg - if (_target != d._target) - return false; - - // fall through method handle check - } - else - { - if (d._methodPtrAux == IntPtr.Zero) - return false; // different delegate kind - - // Ignore the target as it will be the delegate instance, though it may be a different one - /* - if (_methodPtr != d._methodPtr) - return false; - */ - - if (_methodPtrAux == d._methodPtrAux) - return true; - - // fall through method handle check - } - - return MethodDesc == d.MethodDesc; - } - - public override int GetHashCode() - { - int hashCode = MethodDesc.GetHashCode(); - if (_methodPtrAux == IntPtr.Zero && _target != null) - { - hashCode += RuntimeHelpers.GetHashCode(_target) * 33; - } - return hashCode; - } - - protected virtual MethodInfo GetMethodImpl() - { - return Cache.s_methodCache.TryGetValue(this, out MethodInfo? cachedValue) ? cachedValue : GetMethodImplUncached(); - - [MethodImpl(MethodImplOptions.NoInlining)] - MethodInfo GetMethodImplUncached() - { - Debug.Assert(this is MulticastDelegate); - MethodInfo method = Unsafe.As(this).GetMethodImplMulticast(); - - Debug.Assert(method is not null); - Cache.s_methodCache.AddOrUpdate(this, method); - return method; - } - } - - internal MethodInfo GetMethodImplSimple() - { - IRuntimeMethodInfo method = CreateMethodInfo(MethodDesc); - RuntimeType? declaringType = RuntimeMethodHandle.GetDeclaringType(method); - - // need a proper declaring type instance method on a generic type - if (declaringType.IsGenericType) - { - bool isStatic = (RuntimeMethodHandle.GetAttributes(method) & MethodAttributes.Static) != (MethodAttributes)0; - if (!isStatic) - { - if (_methodPtrAux == IntPtr.Zero) - { - // The target may be of a derived type that doesn't have visibility onto the - // target method. We don't want to call RuntimeType.GetMethodBase below with that - // or reflection can end up generating a MethodInfo where the ReflectedType cannot - // see the MethodInfo itself and that breaks an important invariant. But the - // target type could include important generic type information we need in order - // to work out what the exact instantiation of the method's declaring type is. So - // we'll walk up the inheritance chain (which will yield exactly instantiated - // types at each step) until we find the declaring type. Since the declaring type - // we get from the method is probably shared and those in the hierarchy we're - // walking won't be we compare using the generic type definition forms instead. - Type targetType = declaringType.GetGenericTypeDefinition(); - Type? currentType; - for (currentType = _target!.GetType(); currentType != null; currentType = currentType.BaseType) - { - if (currentType.IsGenericType && - currentType.GetGenericTypeDefinition() == targetType) - { - declaringType = currentType as RuntimeType; - break; - } - } - - // RCWs don't need to be "strongly-typed" in which case we don't find a base type - // that matches the declaring type of the method. This is fine because interop needs - // to work with exact methods anyway so declaringType is never shared at this point. - // The targetType may also be an interface with a Default interface method (DIM). - Debug.Assert( - currentType != null - || _target.GetType().IsCOMObject - || targetType.IsInterface, "The class hierarchy should declare the method or be a DIM"); - } - else - { - // it's an open one, need to fetch the first arg of the instantiation - MethodInfo invoke = this.GetType().GetMethod("Invoke")!; - declaringType = (RuntimeType)invoke.GetParametersAsSpan()[0].ParameterType; - } - } - } - return (MethodInfo)RuntimeType.GetMethodBase(declaringType, method)!; - } - - public object? Target => GetTarget(); - - // V1 API. - [RequiresUnreferencedCode("The target method might be removed")] - public static Delegate? CreateDelegate(Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure) - { - ArgumentNullException.ThrowIfNull(type); - ArgumentNullException.ThrowIfNull(target); - ArgumentNullException.ThrowIfNull(method); - - if (type is not RuntimeType rtType) - throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); - if (!rtType.IsDelegate()) - throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); - - Delegate d = InternalAlloc(rtType); - // This API existed in v1/v1.1 and only expected to create closed - // instance delegates. Constrain the call to BindToMethodName to such - // and don't allow relaxed signature matching (which could make the - // choice of target method ambiguous) for backwards compatibility. - // We never generate a closed over null delegate and this is - // actually enforced via the check on target above, but we pass - // NeverCloseOverNull anyway just for clarity. - if (!d.BindToMethodName(target, (RuntimeType)target.GetType(), method, - DelegateBindingFlags.InstanceMethodOnly | - DelegateBindingFlags.ClosedDelegateOnly | - DelegateBindingFlags.NeverCloseOverNull | - (ignoreCase ? DelegateBindingFlags.CaselessMatching : 0))) - { - if (throwOnBindFailure) - throw new ArgumentException(SR.Arg_DlgtTargMeth); - - return null; - } - - return d; - } - - // V1 API. - public static Delegate? CreateDelegate(Type type, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllMethods)] Type target, string method, bool ignoreCase, bool throwOnBindFailure) - { - ArgumentNullException.ThrowIfNull(type); - ArgumentNullException.ThrowIfNull(target); - ArgumentNullException.ThrowIfNull(method); - - if (target.ContainsGenericParameters) - throw new ArgumentException(SR.Arg_UnboundGenParam, nameof(target)); - if (type is not RuntimeType rtType) - throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); - if (target is not RuntimeType rtTarget) - throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(target)); - - if (!rtType.IsDelegate()) - throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); - - Delegate d = InternalAlloc(rtType); - // This API existed in v1/v1.1 and only expected to create open - // static delegates. Constrain the call to BindToMethodName to such - // and don't allow relaxed signature matching (which could make the - // choice of target method ambiguous) for backwards compatibility. - if (!d.BindToMethodName(null, rtTarget, method, - DelegateBindingFlags.StaticMethodOnly | - DelegateBindingFlags.OpenDelegateOnly | - (ignoreCase ? DelegateBindingFlags.CaselessMatching : 0))) - { - if (throwOnBindFailure) - throw new ArgumentException(SR.Arg_DlgtTargMeth); - - return null; - } - - return d; - } - - // V1 API. - public static Delegate? CreateDelegate(Type type, MethodInfo method, bool throwOnBindFailure) - { - ArgumentNullException.ThrowIfNull(type); - ArgumentNullException.ThrowIfNull(method); - - if (type is not RuntimeType rtType) - throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); - - if (method is not RuntimeMethodInfo rmi) - throw new ArgumentException(SR.Argument_MustBeRuntimeMethodInfo, nameof(method)); - - if (!rtType.IsDelegate()) - throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); - - // This API existed in v1/v1.1 and only expected to create closed - // instance delegates. Constrain the call to BindToMethodInfo to - // open delegates only for backwards compatibility. But we'll allow - // relaxed signature checking and open static delegates because - // there's no ambiguity there (the caller would have to explicitly - // pass us a static method or a method with a non-exact signature - // and the only change in behavior from v1.1 there is that we won't - // fail the call). - Delegate? d = CreateDelegateInternal( - rtType, - rmi, - null, - DelegateBindingFlags.OpenDelegateOnly | DelegateBindingFlags.RelaxedSignature); - - if (d == null && throwOnBindFailure) - throw new ArgumentException(SR.Arg_DlgtTargMeth); - - return d; - } - - // V2 API. - public static Delegate? CreateDelegate(Type type, object? firstArgument, MethodInfo method, bool throwOnBindFailure) - { - ArgumentNullException.ThrowIfNull(type); - ArgumentNullException.ThrowIfNull(method); - - if (type is not RuntimeType rtType) - throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); - - if (method is not RuntimeMethodInfo rmi) - throw new ArgumentException(SR.Argument_MustBeRuntimeMethodInfo, nameof(method)); - - if (!rtType.IsDelegate()) - throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); - - // This API is new in Whidbey and allows the full range of delegate - // flexability (open or closed delegates binding to static or - // instance methods with relaxed signature checking. The delegate - // can also be closed over null. There's no ambiguity with all these - // options since the caller is providing us a specific MethodInfo. - Delegate? d = CreateDelegateInternal( - rtType, - rmi, - firstArgument, - DelegateBindingFlags.RelaxedSignature); - - if (d == null && throwOnBindFailure) - throw new ArgumentException(SR.Arg_DlgtTargMeth); - - return d; - } - - // - // internal implementation details (FCALLS and utilities) - // - - // V2 internal API. - internal static Delegate CreateDelegateNoSecurityCheck(Type type, object? target, RuntimeMethodHandle method) - { - ArgumentNullException.ThrowIfNull(type); - - if (method.IsNullHandle()) - throw new ArgumentNullException(nameof(method)); - - if (type is not RuntimeType rtType) - throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(type)); - - if (!rtType.IsDelegate()) - throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type)); - - // Initialize the method... - Delegate d = InternalAlloc(rtType); - // This is a new internal API added in Whidbey. Currently it's only - // used by the dynamic method code to generate a wrapper delegate. - // Allow flexible binding options since the target method is - // unambiguously provided to us. - - if (!d.BindToMethodInfo(target, - method.GetMethodInfo(), - RuntimeMethodHandle.GetDeclaringType(method.GetMethodInfo()), - DelegateBindingFlags.RelaxedSignature)) - throw new ArgumentException(SR.Arg_DlgtTargMeth); - return d; - } - - internal static Delegate? CreateDelegateInternal(RuntimeType rtType, RuntimeMethodInfo rtMethod, object? firstArgument, DelegateBindingFlags flags) - { - Delegate d = InternalAlloc(rtType); - - if (d.BindToMethodInfo(firstArgument, rtMethod, rtMethod.GetDeclaringTypeInternal(), flags)) - return d; - else - return null; - } - - // - // internal implementation details (FCALLS and utilities) - // - - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2067:ParameterDoesntMeetParameterRequirements", - Justification = "The parameter 'methodType' is passed by ref to QCallTypeHandle")] - private bool BindToMethodName(object? target, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllMethods)] RuntimeType methodType, string method, DelegateBindingFlags flags) - { - Delegate d = this; - return BindToMethodName(ObjectHandleOnStack.Create(ref d), ObjectHandleOnStack.Create(ref target), - new QCallTypeHandle(ref methodType), method, flags); - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_BindToMethodName", StringMarshalling = StringMarshalling.Utf8)] - [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool BindToMethodName(ObjectHandleOnStack d, ObjectHandleOnStack target, QCallTypeHandle methodType, string method, DelegateBindingFlags flags); - - private bool BindToMethodInfo(object? target, IRuntimeMethodInfo method, RuntimeType methodType, DelegateBindingFlags flags) - { - Delegate d = this; - bool ret = BindToMethodInfo(ObjectHandleOnStack.Create(ref d), ObjectHandleOnStack.Create(ref target), - method.Value, new QCallTypeHandle(ref methodType), flags); - GC.KeepAlive(method); - return ret; - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_BindToMethodInfo")] - [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool BindToMethodInfo(ObjectHandleOnStack d, ObjectHandleOnStack target, RuntimeMethodHandleInternal method, QCallTypeHandle methodType, DelegateBindingFlags flags); - - private static MulticastDelegate InternalAlloc(RuntimeType type) - { - Debug.Assert(type.IsAssignableTo(typeof(MulticastDelegate))); - return Unsafe.As(RuntimeTypeHandle.InternalAlloc(type)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static unsafe bool InternalEqualTypes(object a, object b) - { - if (a.GetType() == b.GetType()) - return true; -#if FEATURE_TYPEEQUIVALENCE - MethodTable* pMTa = RuntimeHelpers.GetMethodTable(a); - MethodTable* pMTb = RuntimeHelpers.GetMethodTable(b); - - bool ret; - - // only use QCall to check the type equivalence scenario - if (pMTa->HasTypeEquivalence && pMTb->HasTypeEquivalence) - ret = RuntimeHelpers.AreTypesEquivalent(pMTa, pMTb); - else - ret = false; - - GC.KeepAlive(a); - GC.KeepAlive(b); - - return ret; -#else - return false; -#endif // FEATURE_TYPEEQUIVALENCE - } - - // Used by the ctor. Do not call directly. - // The name of this function will appear in managed stacktraces as delegate constructor. - private void DelegateConstruct(object target, IntPtr method) - { - // Via reflection you can pass in just about any value for the method. - // We can do some basic verification up front to prevent EE exceptions. - if (method == IntPtr.Zero) - { - throw new ArgumentNullException(nameof(method)); - } - - Delegate _this = this; - Construct(ObjectHandleOnStack.Create(ref _this), ObjectHandleOnStack.Create(ref target), method); - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_Construct")] - private static partial void Construct(ObjectHandleOnStack _this, ObjectHandleOnStack target, IntPtr method); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe void* GetMulticastInvoke(MethodTable* pMT); - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_GetMulticastInvokeSlow")] - private static unsafe partial void* GetMulticastInvokeSlow(MethodTable* pMT); - - internal unsafe IntPtr GetMulticastInvoke() - { - MethodTable* pMT = RuntimeHelpers.GetMethodTable(this); - void* ptr = GetMulticastInvoke(pMT); - if (ptr == null) - { - ptr = GetMulticastInvokeSlow(pMT); - Debug.Assert(ptr != null); - Debug.Assert(ptr == GetMulticastInvoke(pMT)); - } - // No GC.KeepAlive() since the caller must keep instance alive to use returned pointer. - return (IntPtr)ptr; - } - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe void* GetInvokeMethod(MethodTable* pMT); - - internal unsafe IntPtr GetInvokeMethod() - { - MethodTable* pMT = RuntimeHelpers.GetMethodTable(this); - void* ptr = GetInvokeMethod(pMT); - // No GC.KeepAlive() since the caller must keep instance alive to use returned pointer. - return (IntPtr)ptr; - } - - internal static IRuntimeMethodInfo CreateMethodInfo(IntPtr methodDesc) - { - IRuntimeMethodInfo? methodInfo = null; - CreateMethodInfo(methodDesc, ObjectHandleOnStack.Create(ref methodInfo)); - return methodInfo!; - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_CreateMethodInfo")] - private static partial void CreateMethodInfo(IntPtr methodDesc, ObjectHandleOnStack retMethodInfo); - - [MethodImpl(MethodImplOptions.NoInlining)] - private IntPtr GetMethodDesc() - { - Delegate d = this; - IntPtr desc = GetMethodDesc(ObjectHandleOnStack.Create(ref d)); - _methodDesc = desc; - return desc; - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_GetMethodDesc")] - private static partial IntPtr GetMethodDesc(ObjectHandleOnStack instance); - - internal static IntPtr AdjustTarget(object target, IntPtr methodPtr) - { - return AdjustTarget(ObjectHandleOnStack.Create(ref target), methodPtr); - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_AdjustTarget")] - private static partial IntPtr AdjustTarget(ObjectHandleOnStack target, IntPtr methodPtr); - - internal void InitializeVirtualCallStub(IntPtr methodPtr) - { - Delegate d = this; - InitializeVirtualCallStub(ObjectHandleOnStack.Create(ref d), methodPtr); - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_InitializeVirtualCallStub")] - private static partial void InitializeVirtualCallStub(ObjectHandleOnStack d, IntPtr methodPtr); - - internal virtual object? GetTarget() - { - return (_methodPtrAux == IntPtr.Zero) ? _target : null; - } - - // Caches MethodInfos, added either after first request or assigned from a DynamicMethod - private static class Cache - { - public static readonly ConditionalWeakTable s_methodCache = new(); - } - } - - // These flags effect the way BindToMethodInfo and BindToMethodName are allowed to bind a delegate to a target method. Their - // values must be kept in sync with the definition in vm\comdelegate.h. - internal enum DelegateBindingFlags - { - StaticMethodOnly = 0x00000001, // Can only bind to static target methods - InstanceMethodOnly = 0x00000002, // Can only bind to instance (including virtual) methods - OpenDelegateOnly = 0x00000004, // Only allow the creation of delegates open over the 1st argument - ClosedDelegateOnly = 0x00000008, // Only allow the creation of delegates closed over the 1st argument - NeverCloseOverNull = 0x00000010, // A null target will never been considered as a possible null 1st argument - CaselessMatching = 0x00000020, // Use case insensitive lookup for methods matched by name - RelaxedSignature = 0x00000040, // Allow relaxed signature matching (co/contra variance) - } -} diff --git a/src/coreclr/vm/object.h.bak b/src/coreclr/vm/object.h.bak deleted file mode 100644 index 89a1de721ae0d4..00000000000000 --- a/src/coreclr/vm/object.h.bak +++ /dev/null @@ -1,2586 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// -// OBJECT.H -// -// Definitions of a CLR Object -// - -// See code:EEStartup#TableOfContents for overview - - -#ifndef _OBJECT_H_ -#define _OBJECT_H_ - -#include "util.hpp" -#include "syncblk.h" -#include "gcdesc.h" -#include "sstring.h" -#include "daccess.h" -#include "fcall.h" - -extern "C" void __fastcall ZeroMemoryInGCHeap(void*, size_t); - -void ErectWriteBarrierForMT(MethodTable **dst, MethodTable *ref); - -/* - #ObjectModel - * CLR Internal Object Model - * - * - * Object - This is the common base part to all CLR objects - * | it contains the MethodTable pointer and the - * | sync block index, which is at a negative offset - * | - * +-- StringObject - String objects are specialized objects for string - * | storage/retrieval for higher performance (UCS-2 / UTF-16 data) - * | - * +-- ReflectClassBaseObject - The base object for the RuntimeType class - * | - * +-- ReflectMethodObject - The base object for the RuntimeMethodInfo class - * | - * +-- ReflectFieldObject - The base object for the RtFieldInfo class - * | - * +-- ArrayBase - Base portion of all arrays - * | | - * | +- I1Array - Base type SZ arrays - * | | I2Array - * | | ... - * | | - * | +- PtrArray - SZ Array of OBJECTREFs, different than base arrays because of pObjectClass - * | - * +-- AssemblyBaseObject - The base object for the class Assembly - * | - * | ... - * - * - * PLEASE NOTE THE FOLLOWING WHEN ADDING A NEW OBJECT TYPE: - * - * The size of the object in the heap must be able to be computed - * very, very quickly for GC purposes. Restrictions on the layout - * of the object guarantee this is possible. - * - * Any object that inherits from Object must be able to - * compute its complete size by using the first 4 bytes of - * the object following the Object part and constants - * reachable from the MethodTable... - * - * The formula used for this calculation is: - * MT->GetBaseSize() + ((OBJECTTYPEREF->GetSizeField() * MT->GetComponentSize()) - * - * So for Object, since this is of fixed size, the ComponentSize is 0, which makes the right side - * of the equation above equal to 0 no matter what the value of GetSizeField(), so the size is just the base size. - * - */ - -class MethodTable; -class Thread; -class Assembly; -class AssemblyNative; -class WaitHandleNative; -class ArgDestination; - -struct RCW; - -#ifdef TARGET_64BIT -#define OBJHEADER_SIZE (sizeof(DWORD) /* m_alignpad */ + sizeof(DWORD) /* m_SyncBlockValue */) -#else -#define OBJHEADER_SIZE sizeof(DWORD) /* m_SyncBlockValue */ -#endif - -#define OBJECT_SIZE TARGET_POINTER_SIZE /* m_pMethTab */ -#define OBJECT_BASESIZE (OBJHEADER_SIZE + OBJECT_SIZE) - -#ifdef TARGET_64BIT -#define ARRAYBASE_SIZE (OBJECT_SIZE /* m_pMethTab */ + sizeof(DWORD) /* m_NumComponents */ + sizeof(DWORD) /* pad */) -#else -#define ARRAYBASE_SIZE (OBJECT_SIZE /* m_pMethTab */ + sizeof(DWORD) /* m_NumComponents */) -#endif - -#define ARRAYBASE_BASESIZE (OBJHEADER_SIZE + ARRAYBASE_SIZE) - -// -// The generational GC requires that every object be at least 12 bytes -// in size. - -#define MIN_OBJECT_SIZE (2*TARGET_POINTER_SIZE + OBJHEADER_SIZE) - -#define PTRALIGNCONST (DATA_ALIGNMENT-1) - -#ifndef PtrAlign -#define PtrAlign(size) \ - (((size) + PTRALIGNCONST) & (~PTRALIGNCONST)) -#endif //!PtrAlign - -// code:Object is the representation of an managed object on the GC heap. -// -// See code:#ObjectModel for some important subclasses of code:Object -// -// The only fields mandated by all objects are -// -// * a pointer to the code:MethodTable at offset 0 -// * a pointer to a code:ObjHeader at a negative offset. This is often zero. It holds information that -// any addition information that we might need to attach to arbitrary objects. -// -class Object -{ - friend class CheckAsmOffsets; - - protected: - PTR_MethodTable m_pMethTab; - - protected: - Object() { LIMITED_METHOD_CONTRACT; }; - ~Object() { LIMITED_METHOD_CONTRACT; }; - - public: - MethodTable *RawGetMethodTable() const - { - return m_pMethTab; - } - -#ifndef DACCESS_COMPILE - void RawSetMethodTable(MethodTable *pMT) - { - LIMITED_METHOD_CONTRACT; - m_pMethTab = pMT; - } - - VOID SetMethodTable(MethodTable *pMT) - { - WRAPPER_NO_CONTRACT; - RawSetMethodTable(pMT); - } - - VOID SetMethodTableForUOHObject(MethodTable *pMT) - { - WRAPPER_NO_CONTRACT; - // This function must be used if the allocation occurs on a UOH heap, and the method table might be a collectible type - ErectWriteBarrierForMT(&m_pMethTab, pMT); - } -#endif //!DACCESS_COMPILE - -#define MARKED_BIT 0x1 - - PTR_MethodTable GetMethodTable() const - { - LIMITED_METHOD_DAC_CONTRACT; - -#ifndef DACCESS_COMPILE - // We should always use GetGCSafeMethodTable() if we're running during a GC. - // If the mark bit is set then we're running during a GC - _ASSERTE((dac_cast(m_pMethTab) & MARKED_BIT) == 0); - - return m_pMethTab; -#else //DACCESS_COMPILE - - //@dbgtodo dharvey Make this a type which supports bitwise and operations - //when available - return PTR_MethodTable((dac_cast(m_pMethTab)) & (~MARKED_BIT)); -#endif //DACCESS_COMPILE - } - - DPTR(PTR_MethodTable) GetMethodTablePtr() const - { - LIMITED_METHOD_CONTRACT; - return dac_cast(PTR_HOST_MEMBER_TADDR(Object, this, m_pMethTab)); - } - - TypeHandle GetTypeHandle(); - - // Methods used to determine if an object supports a given interface. - static BOOL SupportsInterface(OBJECTREF pObj, MethodTable *pInterfaceMT); - - inline DWORD GetNumComponents(); - inline SIZE_T GetSize(); - - CGCDesc* GetSlotMap() - { - WRAPPER_NO_CONTRACT; - return( CGCDesc::GetCGCDescFromMT(GetMethodTable())); - } - - // Sync Block & Synchronization services - - // Access the ObjHeader which is at a negative offset on the object (because of - // cache lines) - PTR_ObjHeader GetHeader() - { - LIMITED_METHOD_DAC_CONTRACT; - return dac_cast(this) - 1; - } - - // Get the current address of the object (works for debug refs, too.) - PTR_BYTE GetAddress() - { - LIMITED_METHOD_DAC_CONTRACT; - return dac_cast(this); - } - -#ifdef _DEBUG - // TRUE if the header has a real SyncBlockIndex (i.e. it has an entry in the - // SyncTable, though it doesn't necessarily have an entry in the SyncBlockCache) - BOOL HasEmptySyncBlockInfo() - { - WRAPPER_NO_CONTRACT; - return GetHeader()->HasEmptySyncBlockInfo(); - } -#endif - - // retrieve or allocate a sync block for this object - SyncBlock *GetSyncBlock() - { - WRAPPER_NO_CONTRACT; - return GetHeader()->GetSyncBlock(); - } - - DWORD GetSyncBlockIndex() - { - WRAPPER_NO_CONTRACT; - return GetHeader()->GetSyncBlockIndex(); - } - - // DO NOT ADD ANY ASSERTS TO THIS METHOD. - // DO NOT USE THIS METHOD. - // Yes folks, for better or worse the debugger pokes supposed object addresses - // to try to see if objects are valid, possibly firing an AccessViolation or worse, - // and then catches the AV and reports a failure to the debug client. This makes - // the debugger slightly more robust should any corrupted object references appear - // in a session. Thus it is "correct" behaviour for this to AV when used with - // an invalid object pointer, and incorrect behaviour for it to - // assert. - BOOL ValidateObjectWithPossibleAV(); - - // Validate an object ref out of the VerifyHeap routine in the GC - void ValidateHeap(BOOL bDeep=TRUE); - - PTR_SyncBlock PassiveGetSyncBlock() - { - LIMITED_METHOD_DAC_CONTRACT; - return GetHeader()->PassiveGetSyncBlock(); - } - - static DWORD ComputeHashCode(); - static DWORD GetGlobalNewHashCode(); - - inline INT32 TryGetHashCode(); -#ifndef DACCESS_COMPILE - INT32 GetHashCodeEx(); -#endif // #ifndef DACCESS_COMPILE - - PTR_VOID UnBox(); // if it is a value class, get the pointer to the first field - - PTR_BYTE GetData(void) - { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - return dac_cast(this) + sizeof(Object); - } - - static UINT GetOffsetOfFirstField() - { - LIMITED_METHOD_CONTRACT; - return sizeof(Object); - } - - DWORD GetOffset32(DWORD dwOffset) - { - WRAPPER_NO_CONTRACT; - return * PTR_DWORD(GetData() + dwOffset); - } - - USHORT GetOffset16(DWORD dwOffset) - { - WRAPPER_NO_CONTRACT; - return * PTR_USHORT(GetData() + dwOffset); - } - - BYTE GetOffset8(DWORD dwOffset) - { - WRAPPER_NO_CONTRACT; - return * PTR_BYTE(GetData() + dwOffset); - } - - int64_t GetOffset64(DWORD dwOffset) - { - WRAPPER_NO_CONTRACT; - return (int64_t) * PTR_ULONG64(GetData() + dwOffset); - } - - void *GetPtrOffset(DWORD dwOffset) - { - WRAPPER_NO_CONTRACT; - return (void *)(TADDR)*PTR_TADDR(GetData() + dwOffset); - } - -#ifndef DACCESS_COMPILE - - void SetOffsetObjectRef(DWORD dwOffset, size_t dwValue); - - void SetOffsetPtr(DWORD dwOffset, LPVOID value) - { - WRAPPER_NO_CONTRACT; - *(LPVOID *) &GetData()[dwOffset] = value; - } - - void SetOffset32(DWORD dwOffset, DWORD dwValue) - { - WRAPPER_NO_CONTRACT; - *(DWORD *) &GetData()[dwOffset] = dwValue; - } - - void SetOffset16(DWORD dwOffset, DWORD dwValue) - { - WRAPPER_NO_CONTRACT; - *(USHORT *) &GetData()[dwOffset] = (USHORT) dwValue; - } - - void SetOffset8(DWORD dwOffset, DWORD dwValue) - { - WRAPPER_NO_CONTRACT; - *(BYTE *) &GetData()[dwOffset] = (BYTE) dwValue; - } - - void SetOffset64(DWORD dwOffset, int64_t qwValue) - { - WRAPPER_NO_CONTRACT; - *(int64_t *) &GetData()[dwOffset] = qwValue; - } - -#endif // #ifndef DACCESS_COMPILE - - VOID Validate(BOOL bDeep = TRUE, BOOL bVerifyNextHeader = TRUE, BOOL bVerifySyncBlock = TRUE); - - PTR_MethodTable GetGCSafeMethodTable() const - { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - - // lose GC marking bit and the reserved bit - // A method table pointer should always be aligned. During GC we set the least - // significant bit for marked objects, and the second to least significant - // bit is reserved. So if we want the actual MT pointer during a GC - // we must zero out the lowest 2 bits on 32-bit and 3 bits on 64-bit. -#ifdef TARGET_64BIT - return dac_cast((dac_cast(m_pMethTab)) & ~((UINT_PTR)7)); -#else - return dac_cast((dac_cast(m_pMethTab)) & ~((UINT_PTR)3)); -#endif //TARGET_64BIT - } - - // There are some cases where it is unsafe to get the type handle during a GC. - // This occurs when the type has already been unloaded as part of an in-progress appdomain shutdown. - TypeHandle GetGCSafeTypeHandleIfPossible() const; - - inline TypeHandle GetGCSafeTypeHandle() const; - -#ifdef DACCESS_COMPILE - void EnumMemoryRegions(void); -#endif - - private: - VOID ValidateInner(BOOL bDeep, BOOL bVerifyNextHeader, BOOL bVerifySyncBlock); - - friend struct ::cdac_data; -}; - -template<> -struct cdac_data -{ - static constexpr size_t m_pMethTab = offsetof(Object, m_pMethTab); -}; - -/* - * Object ref setting routines. You must use these to do - * proper write barrier support. - */ - -// SetObjectReference sets an OBJECTREF field - -void SetObjectReferenceUnchecked(OBJECTREF *dst,OBJECTREF ref); - -#ifdef _DEBUG -void EnableStressHeapHelper(); -#endif - -//Used to clear the object reference -inline void ClearObjectReference(OBJECTREF* dst) -{ - LIMITED_METHOD_CONTRACT; - *(void**)(dst) = NULL; -} - -// CopyValueClass sets a value class field - -void CopyValueClassUnchecked(void* dest, void* src, MethodTable *pMT); -void CopyValueClassArgUnchecked(ArgDestination *argDest, void* src, MethodTable *pMT, int destOffset); - -inline void InitValueClass(void *dest, MethodTable *pMT) -{ - WRAPPER_NO_CONTRACT; - ZeroMemoryInGCHeap(dest, pMT->GetNumInstanceFieldBytes()); -} - -// Initialize value class argument -void InitValueClassArg(ArgDestination *argDest, MethodTable *pMT); - -#define SetObjectReference(_d,_r) SetObjectReferenceUnchecked(_d, _r) -#define CopyValueClass(_d,_s,_m) CopyValueClassUnchecked(_d,_s,_m) -#define CopyValueClassArg(_d,_s,_m,_o) CopyValueClassArgUnchecked(_d,_s,_m,_o) - -#include - - -// There are two basic kinds of array layouts in CLR -// ELEMENT_TYPE_ARRAY - a multidimensional array with lower bounds on the dims -// ELMENNT_TYPE_SZARRAY - A zero based single dimensional array -// -// In addition the layout of an array in memory is also affected by -// whether the method table is shared (eg in the case of arrays of object refs) -// or not. In the shared case, the array has to hold the type handle of -// the element type. -// -// ArrayBase encapuslates all of these details. In theory you should never -// have to peek inside this abstraction -// -class ArrayBase : public Object -{ - friend class GCHeap; - friend class CObjectHeader; - friend class Object; - friend OBJECTREF AllocateSzArray(MethodTable *pArrayMT, INT32 length, GC_ALLOC_FLAGS flags); - friend OBJECTREF TryAllocateFrozenSzArray(MethodTable* pArrayMT, INT32 length); - friend class JIT_TrialAlloc; - friend class CheckAsmOffsets; - friend struct _DacGlobals; - -private: - // This MUST be the first field, so that it directly follows Object. This is because - // Object::GetSize() looks at m_NumComponents even though it may not be an array (the - // values is shifted out if not an array, so it's ok). - DWORD m_NumComponents; -#ifdef TARGET_64BIT - DWORD pad; -#endif // TARGET_64BIT - - SVAL_DECL(INT32, s_arrayBoundsZero); // = 0 - - // What comes after this conceputally is: - // INT32 bounds[rank]; The bounds are only present for Multidimensional arrays - // INT32 lowerBounds[rank]; Valid indexes are lowerBounds[i] <= index[i] < lowerBounds[i] + bounds[i] - -public: -#ifndef DACCESS_COMPILE - void SetNumComponents(INT32 length) - { - LIMITED_METHOD_CONTRACT; - m_NumComponents = length; - } -#endif // !DACCESS_COMPILE - - // Get the element type for the array, this works whether the element - // type is stored in the array or not - inline TypeHandle GetArrayElementTypeHandle() const; - - // Get the CorElementType for the elements in the array. Avoids creating a TypeHandle - inline CorElementType GetArrayElementType() const; - - inline unsigned GetRank() const; - - // Total element count for the array - inline DWORD GetNumComponents() const; - - // Get pointer to elements, handles any number of dimensions - PTR_BYTE GetGCSafeDataPtr() const { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; -#ifdef _DEBUG -#ifndef DACCESS_COMPILE - EnableStressHeapHelper(); -#endif -#endif - return dac_cast(this) + - GetDataPtrOffset(GetGCSafeMethodTable()); - } - - PTR_BYTE GetDataPtr() const { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; -#ifdef _DEBUG -#ifndef DACCESS_COMPILE - EnableStressHeapHelper(); -#endif -#endif - PTR_BYTE result = dac_cast(this) + - GetDataPtrOffset(GetMethodTable()); - _ASSERTE(result == GetGCSafeDataPtr()); - return result; - } - - // The component size is actually 16-bit WORD, but this method is returning SIZE_T to ensure - // that SIZE_T is used everywhere for object size computation. It is necessary to support - // objects bigger than 2GB. - SIZE_T GetComponentSize() const { - WRAPPER_NO_CONTRACT; - MethodTable * pMT; - pMT = GetMethodTable(); - _ASSERTE(pMT->HasComponentSize()); - return pMT->RawGetComponentSize(); - } - - // Note that this can be a multidimensional array of rank 1 - // (for example if we had a 1-D array with lower bounds - BOOL IsMultiDimArray() const { - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - return(GetMethodTable()->IsMultiDimArray()); - } - - // Get pointer to the beginning of the bounds (counts for each dim) - // Works for any array type - PTR_INT32 GetBoundsPtr() const { - WRAPPER_NO_CONTRACT; - MethodTable * pMT = GetMethodTable(); - if (pMT->IsMultiDimArray()) - { - return dac_cast( - dac_cast(this) + sizeof(*this)); - } - else - { - return dac_cast(PTR_HOST_MEMBER_TADDR(ArrayBase, this, - m_NumComponents)); - } - } - - // Works for any array type - PTR_INT32 GetLowerBoundsPtr() const { - WRAPPER_NO_CONTRACT; - if (IsMultiDimArray()) - { - // Lower bounds info is after total bounds info - // and total bounds info has rank elements - return GetBoundsPtr() + GetRank(); - } - else - return dac_cast(GVAL_ADDR(s_arrayBoundsZero)); - } - - static unsigned GetOffsetOfNumComponents() { - LIMITED_METHOD_CONTRACT; - return offsetof(ArrayBase, m_NumComponents); - } - - inline static unsigned GetDataPtrOffset(MethodTable* pMT); - - inline static unsigned GetBoundsOffset(MethodTable* pMT); - inline static unsigned GetLowerBoundsOffset(MethodTable* pMT); - - friend struct ::cdac_data; -}; - -#ifndef DACCESS_COMPILE -template<> -struct cdac_data -{ - static constexpr size_t m_NumComponents = offsetof(ArrayBase, m_NumComponents); - - static constexpr INT32* ArrayBoundsZero = &ArrayBase::s_arrayBoundsZero; -}; -#endif - -// -// Template used to build all the non-object -// arrays of a single dimension -// - -template < class KIND > -class Array : public ArrayBase -{ - public: - - typedef DPTR(KIND) PTR_KIND; - - KIND m_Array[1]; - - PTR_KIND GetDirectPointerToNonObjectElements() const - { - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - return dac_cast(PTR_HOST_MEMBER_TADDR(Array, this, m_Array)); - } -}; - - -// Warning: Use PtrArray only for single dimensional arrays, not multidim arrays. -class PtrArray : public ArrayBase -{ - friend class GCHeap; - friend class ClrDataAccess; - friend class JIT_TrialAlloc; - friend class CheckAsmOffsets; - -public: - TypeHandle GetArrayElementTypeHandle() - { - LIMITED_METHOD_CONTRACT; - return GetMethodTable()->GetArrayElementTypeHandle(); - } - - PTR_OBJECTREF GetDataPtr() - { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - return dac_cast(dac_cast(this) + GetDataOffset()); - } - - static SIZE_T GetDataOffset() - { - LIMITED_METHOD_CONTRACT; - return offsetof(PtrArray, m_Array); - } - - void SetAt(SIZE_T i, OBJECTREF ref) - { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - } - CONTRACTL_END; - _ASSERTE(i < GetNumComponents()); - SetObjectReference(m_Array + i, ref); - } - - void ClearAt(SIZE_T i) - { - WRAPPER_NO_CONTRACT; - _ASSERTE(i < GetNumComponents()); - ClearObjectReference(m_Array + i); - } - - OBJECTREF GetAt(SIZE_T i) - { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - _ASSERTE(i < GetNumComponents()); - -// DAC doesn't know the true size of this array -// the compiler thinks it is size 1, but really it is size N hanging off the structure -#ifndef DACCESS_COMPILE - return m_Array[i]; -#else - TADDR arrayTargetAddress = dac_cast(this) + offsetof(PtrArray, m_Array); - __ArrayDPtr targetArray = dac_cast< __ArrayDPtr >(arrayTargetAddress); - return targetArray[i]; -#endif - } - - friend class StubLinkerCPU; - friend class ArrayOpLinker; -public: - OBJECTREF m_Array[1]; -}; - -#define OFFSETOF__PtrArray__m_Array_ ARRAYBASE_SIZE - -/* a TypedByRef is a structure that is used to implement VB's BYREF variants. - it is basically a tuple of an address of some data along with a TypeHandle - that indicates the type of the address */ -class TypedByRef -{ -public: - - PTR_VOID data; - TypeHandle type; -}; - -typedef DPTR(TypedByRef) PTR_TypedByRef; - -typedef Array I1Array; -typedef Array I2Array; -typedef Array I4Array; -typedef Array I8Array; -typedef Array R4Array; -typedef Array R8Array; -typedef Array U1Array; -typedef Array BOOLArray; -typedef Array U2Array; -typedef Array CHARArray; -typedef Array U4Array; -typedef Array U8Array; -typedef Array UPTRArray; -typedef PtrArray PTRArray; - -typedef DPTR(I1Array) PTR_I1Array; -typedef DPTR(I2Array) PTR_I2Array; -typedef DPTR(I4Array) PTR_I4Array; -typedef DPTR(I8Array) PTR_I8Array; -typedef DPTR(R4Array) PTR_R4Array; -typedef DPTR(R8Array) PTR_R8Array; -typedef DPTR(U1Array) PTR_U1Array; -typedef DPTR(BOOLArray) PTR_BOOLArray; -typedef DPTR(U2Array) PTR_U2Array; -typedef DPTR(CHARArray) PTR_CHARArray; -typedef DPTR(U4Array) PTR_U4Array; -typedef DPTR(U8Array) PTR_U8Array; -typedef DPTR(UPTRArray) PTR_UPTRArray; -typedef DPTR(PTRArray) PTR_PTRArray; - -class StringObject; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF BASEARRAYREF; -typedef REF I1ARRAYREF; -typedef REF I2ARRAYREF; -typedef REF I4ARRAYREF; -typedef REF I8ARRAYREF; -typedef REF R4ARRAYREF; -typedef REF R8ARRAYREF; -typedef REF U1ARRAYREF; -typedef REF BOOLARRAYREF; -typedef REF U2ARRAYREF; -typedef REF U4ARRAYREF; -typedef REF U8ARRAYREF; -typedef REF UPTRARRAYREF; -typedef REF CHARARRAYREF; -typedef REF PTRARRAYREF; // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays. -typedef REF STRINGREF; - -#else // USE_CHECKED_OBJECTREFS - -typedef PTR_ArrayBase BASEARRAYREF; -typedef PTR_I1Array I1ARRAYREF; -typedef PTR_I2Array I2ARRAYREF; -typedef PTR_I4Array I4ARRAYREF; -typedef PTR_I8Array I8ARRAYREF; -typedef PTR_R4Array R4ARRAYREF; -typedef PTR_R8Array R8ARRAYREF; -typedef PTR_U1Array U1ARRAYREF; -typedef PTR_BOOLArray BOOLARRAYREF; -typedef PTR_U2Array U2ARRAYREF; -typedef PTR_U4Array U4ARRAYREF; -typedef PTR_U8Array U8ARRAYREF; -typedef PTR_UPTRArray UPTRARRAYREF; -typedef PTR_CHARArray CHARARRAYREF; -typedef PTR_PTRArray PTRARRAYREF; // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays. -typedef PTR_StringObject STRINGREF; - -#endif // USE_CHECKED_OBJECTREFS - - -#include - - -/* - * StringObject - * - * Special String implementation for performance. - * - * m_StringLength - Length of string in number of WCHARs - * m_FirstChar - The string buffer - * - */ - - -class StringObject : public Object -{ -#ifdef DACCESS_COMPILE - friend class ClrDataAccess; -#endif - friend class GCHeap; - friend class JIT_TrialAlloc; - friend class CheckAsmOffsets; - friend class COMString; - - private: - DWORD m_StringLength; - WCHAR m_FirstChar; - - public: - VOID SetStringLength(DWORD len) { LIMITED_METHOD_CONTRACT; _ASSERTE(len >= 0); m_StringLength = len; } - - protected: - StringObject() {LIMITED_METHOD_CONTRACT; } - ~StringObject() {LIMITED_METHOD_CONTRACT; } - - public: - static DWORD GetBaseSize(); - static SIZE_T GetSize(DWORD stringLength); - - DWORD GetStringLength() { LIMITED_METHOD_DAC_CONTRACT; return( m_StringLength );} - WCHAR* GetBuffer() { LIMITED_METHOD_CONTRACT; return (WCHAR*)( dac_cast(this) + offsetof(StringObject, m_FirstChar) ); } - - static UINT GetBufferOffset() - { - LIMITED_METHOD_DAC_CONTRACT; - return (UINT)(offsetof(StringObject, m_FirstChar)); - } - static UINT GetStringLengthOffset() - { - LIMITED_METHOD_CONTRACT; - return (UINT)(offsetof(StringObject, m_StringLength)); - } - VOID GetSString(SString &result) - { - WRAPPER_NO_CONTRACT; - result.Set(GetBuffer(), GetStringLength()); - } - //======================================================================== - // Creates a System.String object. All the functions that take a length - // or a count of bytes will add the null terminator after length - // characters. So this means that if you have a string that has 5 - // characters and the null terminator you should pass in 5 and NOT 6. - //======================================================================== - static STRINGREF NewString(int length); - static STRINGREF NewString(const WCHAR *pwsz); - static STRINGREF NewString(const WCHAR *pwsz, int length); - static STRINGREF NewString(LPCUTF8 psz); - static STRINGREF NewString(LPCUTF8 psz, int cBytes); - - static STRINGREF GetEmptyString(); - static STRINGREF* GetEmptyStringRefPtr(void** pinnedString); - - static STRINGREF* InitEmptyStringRefPtr(); - - /*=================RefInterpretGetStringValuesDangerousForGC====================== - **N.B.: This performs no range checking and relies on the caller to have done this. - **Args: (IN)ref -- the String to be interpretted. - ** (OUT)chars -- a pointer to the characters in the buffer. - ** (OUT)length -- a pointer to the length of the buffer. - **Returns: void. - **Exceptions: None. - ==============================================================================*/ - // !!!! If you use this function, you have to be careful because chars is a pointer - // !!!! to the data buffer of ref. If GC happens after this call, you need to make - // !!!! sure that you have a pin handle on ref, or use GCPROTECT_BEGINPINNING on ref. - void RefInterpretGetStringValuesDangerousForGC(_Outptr_result_buffer_(*length + 1) WCHAR **chars, int *length) { - WRAPPER_NO_CONTRACT; - - _ASSERTE(GetGCSafeMethodTable() == g_pStringClass); - *length = GetStringLength(); - *chars = GetBuffer(); -#ifdef _DEBUG - EnableStressHeapHelper(); -#endif - } - - -private: - static STRINGREF* EmptyStringRefPtr; - static bool EmptyStringIsFrozen; - - friend struct ::cdac_data; -}; - -template<> -struct cdac_data -{ - static constexpr size_t m_FirstChar = offsetof(StringObject, m_FirstChar); - static constexpr size_t m_StringLength = offsetof(StringObject, m_StringLength); -}; - -/*================================GetEmptyString================================ -**Get a reference to the empty string. If we haven't already gotten one, we -**query the String class for a pointer to the empty string that we know was -**created at startup. -** -**Args: None -**Returns: A STRINGREF to the EmptyString -**Exceptions: None -==============================================================================*/ -inline STRINGREF StringObject::GetEmptyString() { - - CONTRACTL { - THROWS; - MODE_COOPERATIVE; - GC_TRIGGERS; - } CONTRACTL_END; - STRINGREF* refptr = EmptyStringRefPtr; - - //If we've never gotten a reference to the EmptyString, we need to go get one. - if (refptr==NULL) { - refptr = InitEmptyStringRefPtr(); - } - //We've already have a reference to the EmptyString, so we can just return it. - return *refptr; -} - -inline STRINGREF* StringObject::GetEmptyStringRefPtr(void** pinnedString) { - - CONTRACTL { - THROWS; - MODE_ANY; - GC_TRIGGERS; - } CONTRACTL_END; - - STRINGREF* refptr = EmptyStringRefPtr; - - //If we've never gotten a reference to the EmptyString, we need to go get one. - if (refptr == nullptr) - { - refptr = InitEmptyStringRefPtr(); - } - - if (EmptyStringIsFrozen && pinnedString != nullptr) - { - *pinnedString = *(void**)refptr; - } - - //We've already have a reference to the EmptyString, so we can just return it. - return refptr; -} - -// This is the Class version of the Reflection object. -// A Class has adddition information. -// For a ReflectClassBaseObject the m_pData is a pointer to a FieldDesc array that -// contains all of the final static primitives if its defined. -// m_cnt = the number of elements defined in the m_pData FieldDesc array. -1 means -// this hasn't yet been defined. -class ReflectClassBaseObject : public Object -{ - friend class CoreLibBinder; - -protected: - OBJECTREF m_keepalive; - OBJECTREF m_cache; - TypeHandle m_typeHandle; - -#ifdef _DEBUG - void TypeCheck() - { - CONTRACTL - { - NOTHROW; - MODE_COOPERATIVE; - GC_NOTRIGGER; - } - CONTRACTL_END; - - MethodTable *pMT = GetMethodTable(); - while (pMT != g_pRuntimeTypeClass && pMT != NULL) - { - pMT = pMT->GetParentMethodTable(); - } - _ASSERTE(pMT == g_pRuntimeTypeClass); - } -#endif // _DEBUG - -public: - void SetType(TypeHandle type) { - CONTRACTL - { - NOTHROW; - MODE_COOPERATIVE; - GC_NOTRIGGER; - } - CONTRACTL_END; - - INDEBUG(TypeCheck()); - m_typeHandle = type; - } - - void SetKeepAlive(OBJECTREF keepalive) - { - CONTRACTL - { - NOTHROW; - MODE_COOPERATIVE; - GC_NOTRIGGER; - } - CONTRACTL_END; - - INDEBUG(TypeCheck()); - SetObjectReference(&m_keepalive, keepalive); - } - - TypeHandle GetType() { - CONTRACTL - { - NOTHROW; - MODE_COOPERATIVE; - GC_NOTRIGGER; - } - CONTRACTL_END; - - INDEBUG(TypeCheck()); - return m_typeHandle; - } - -}; - -// This is the Method version of the Reflection object. -// A Method has additional information: -// m_pMD - A pointer to the actual MethodDesc of the method. -// m_object - a field that has a reference type in it. Used only for RuntimeMethodInfoStub to keep the real type alive. -// This structure matches the structure up to the m_pMD for several different managed types. -// (RuntimeConstructorInfo, RuntimeMethodInfo, and RuntimeMethodInfoStub). These types are unrelated in the type -// system except that they all implement a particular interface. It is important that such interface is not attached to any -// type that does not sufficiently match this data structure. -class ReflectMethodObject : public Object -{ - friend class CoreLibBinder; - -protected: - OBJECTREF m_object; - OBJECTREF m_empty1; - OBJECTREF m_empty2; - OBJECTREF m_empty3; - OBJECTREF m_empty4; - OBJECTREF m_empty5; - OBJECTREF m_empty6; - OBJECTREF m_empty7; - OBJECTREF m_empty8; - MethodDesc * m_pMD; - -public: - void SetMethod(MethodDesc *pMethod) { - LIMITED_METHOD_CONTRACT; - m_pMD = pMethod; - } - - // This must only be called on instances of ReflectMethodObject that are actually RuntimeMethodInfoStub - void SetKeepAlive(OBJECTREF keepalive) - { - WRAPPER_NO_CONTRACT; - SetObjectReference(&m_object, keepalive); - } - - MethodDesc *GetMethod() { - LIMITED_METHOD_CONTRACT; - return m_pMD; - } - -}; - -// This is the Field version of the Reflection object. -// A Method has additional information: -// m_pFD - A pointer to the actual MethodDesc of the method. -// m_object - a field that has a reference type in it. Used only for RuntimeFieldInfoStub to keep the real type alive. -// This structure matches the structure up to the m_pFD for several different managed types. -// (RtFieldInfo and RuntimeFieldInfoStub). These types are unrelated in the type -// system except that they all implement a particular interface. It is important that such interface is not attached to any -// type that does not sufficiently match this data structure. -class ReflectFieldObject : public Object -{ - friend class CoreLibBinder; - -protected: - OBJECTREF m_object; - OBJECTREF m_empty1; - INT32 m_empty2; - OBJECTREF m_empty3; - OBJECTREF m_empty4; - OBJECTREF m_empty5; - FieldDesc * m_pFD; - -public: - void SetField(FieldDesc *pField) { - LIMITED_METHOD_CONTRACT; - m_pFD = pField; - } - - // This must only be called on instances of ReflectFieldObject that are actually RuntimeFieldInfoStub - void SetKeepAlive(OBJECTREF keepalive) - { - WRAPPER_NO_CONTRACT; - SetObjectReference(&m_object, keepalive); - } - - FieldDesc *GetField() { - LIMITED_METHOD_CONTRACT; - return m_pFD; - } -}; - -// ReflectModuleBaseObject -// This class is the base class for managed Module. -// This class will connect the Object back to the underlying VM representation -// m_ReflectClass -- This is the real Class that was used for reflection -// This class was used to get at this object -// m_pData -- this is a generic pointer which usually points CorModule -// -class ReflectModuleBaseObject : public Object -{ - friend class CoreLibBinder; - - protected: - // READ ME: - // Modifying the order or fields of this object may require other changes to the - // classlib class definition of this object. - OBJECTREF m_runtimeType; - OBJECTREF m_runtimeAssembly; - Module* m_pData; // Pointer to the Module - - protected: - ReflectModuleBaseObject() {LIMITED_METHOD_CONTRACT;} - ~ReflectModuleBaseObject() {LIMITED_METHOD_CONTRACT;} - - public: - void SetModule(Module* p) { - LIMITED_METHOD_CONTRACT; - m_pData = p; - } - Module* GetModule() { - LIMITED_METHOD_CONTRACT; - return m_pData; - } - void SetAssembly(OBJECTREF assembly) - { - WRAPPER_NO_CONTRACT; - SetObjectReference(&m_runtimeAssembly, assembly); - } -}; - -class ThreadBaseObject; -class ExecutionContextObject: public Object -{ - friend class CoreLibBinder; - -private: - // These field are also defined in the managed representation. (SecurityContext.cs)If you - // add or change these field you must also change the managed code so that - // it matches these. This is necessary so that the object is the proper - // size. - OBJECTREF m_localValues; - OBJECTREF m_localChangeNotifications; - CLR_BOOL m_isFlowSuppressed; - -public: - bool IsFlowSuppressed() const - { - LIMITED_METHOD_CONTRACT; - return m_isFlowSuppressed; - } -}; - -typedef DPTR(class CultureInfoBaseObject) PTR_CultureInfoBaseObject; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF EXECUTIONCONTEXTREF; -typedef REF ARRAYBASEREF; - -#else -typedef ExecutionContextObject* EXECUTIONCONTEXTREF; -typedef PTR_ArrayBase ARRAYBASEREF; -#endif - -typedef DPTR(class ThreadBaseObject) PTR_ThreadBaseObject; -class ThreadBaseObject : public Object -{ - friend class ClrDataAccess; - friend class CoreLibBinder; - friend class Object; - -private: - - // These field are also defined in the managed representation. If you - // add or change these field you must also change the managed code so that - // it matches these. This is necessary so that the object is the proper - // size. The order here must match that order which the loader will choose - // when laying out the managed class. Note that the layouts are checked - // at run time, not compile time. - OBJECTREF m_ExecutionContext; - OBJECTREF m_SynchronizationContext; - STRINGREF m_Name; - OBJECTREF m_StartHelper; -#ifdef TARGET_UNIX - OBJECTREF m_WaitInfo; - OBJECTREF m_joinEvent; -#endif // TARGET_UNIX - - // The next field (m_InternalThread) is declared as IntPtr in the managed - // definition of Thread. The loader will sort it next. - - // m_InternalThread is always valid -- unless the thread has finalized and been - // resurrected. (The thread stopped running before it was finalized). - Thread *m_InternalThread; - INT32 m_Priority; - - // We need to cache the thread id in managed code for perf reasons. - INT32 m_ManagedThreadId; - - // Only used by managed code, see comment there - bool m_MayNeedResetForThreadPool; - - // Set in unmanaged code and read in managed code. - bool m_IsDead; - - bool m_IsThreadPool; - -protected: - // the ctor and dtor can do no useful work. - ThreadBaseObject() {LIMITED_METHOD_CONTRACT;}; - ~ThreadBaseObject() {LIMITED_METHOD_CONTRACT;}; - -public: - Thread *GetInternal() - { - LIMITED_METHOD_CONTRACT; - return m_InternalThread; - } - - void SetInternal(Thread *it); - void ClearInternal(); - - void SetManagedThreadId(INT32 id) - { - LIMITED_METHOD_CONTRACT; - m_ManagedThreadId = id; - } - - STRINGREF GetName() { - LIMITED_METHOD_CONTRACT; - return m_Name; - } - - OBJECTREF GetExecutionContext() - { - LIMITED_METHOD_CONTRACT; - return m_ExecutionContext; - } - - void InitExisting(); - - void ResetStartHelper() - { - LIMITED_METHOD_CONTRACT - m_StartHelper = NULL; - } - - void SetPriority(INT32 priority) - { - LIMITED_METHOD_CONTRACT; - m_Priority = priority; - } - - INT32 GetPriority() const - { - LIMITED_METHOD_CONTRACT; - return m_Priority; - } - - void SetIsDead() - { - LIMITED_METHOD_CONTRACT; - m_IsDead = true; - } -}; - -// AssemblyBaseObject -// This class is the base class for assemblies -// -class AssemblyBaseObject : public Object -{ - friend class Assembly; - friend class CoreLibBinder; - - protected: - // READ ME: - // Modifying the order or fields of this object may require other changes to the - // classlib class definition of this object. - OBJECTREF m_pModuleEventHandler; // Delegate for 'resolve module' event - STRINGREF m_fullname; // Slot for storing assemblies fullname - OBJECTREF m_pSyncRoot; // Pointer to loader allocator to keep collectible types alive, and to serve as the syncroot for assembly building in ref.emit - Assembly* m_pAssembly; // Pointer to the Assembly Structure - - protected: - AssemblyBaseObject() { LIMITED_METHOD_CONTRACT; } - ~AssemblyBaseObject() { LIMITED_METHOD_CONTRACT; } - - public: - - void SetAssembly(Assembly* p) - { - LIMITED_METHOD_CONTRACT; - m_pAssembly = p; - } - - Assembly* GetAssembly(); - - void SetSyncRoot(OBJECTREF pSyncRoot) - { - WRAPPER_NO_CONTRACT; - SetObjectReference(&m_pSyncRoot, pSyncRoot); - } -}; - -// AssemblyLoadContextBaseObject -// This class is the base class for AssemblyLoadContext -// -#if defined(TARGET_X86) && !defined(TARGET_UNIX) -#include "pshpack4.h" -#endif // defined(TARGET_X86) && !defined(TARGET_UNIX) -class AssemblyLoadContextBaseObject : public Object -{ - friend class CoreLibBinder; - - protected: - // READ ME: - // Modifying the order or fields of this object may require other changes to the - // classlib class definition of this object. -#ifdef TARGET_64BIT - OBJECTREF _unloadLock; - OBJECTREF _resolvingUnmanagedDll; - OBJECTREF _resolving; - OBJECTREF _unloading; - OBJECTREF _name; - INT_PTR _nativeAssemblyLoadContext; - int64_t _id; // On 64-bit platforms this is a value type so it is placed after references and pointers - DWORD _state; - CLR_BOOL _isCollectible; -#else // TARGET_64BIT - int64_t _id; // On 32-bit platforms this 64-bit value type is larger than a pointer so JIT places it first - OBJECTREF _unloadLock; - OBJECTREF _resolvingUnmanagedDll; - OBJECTREF _resolving; - OBJECTREF _unloading; - OBJECTREF _name; - INT_PTR _nativeAssemblyLoadContext; - DWORD _state; - CLR_BOOL _isCollectible; -#endif // TARGET_64BIT - - protected: - AssemblyLoadContextBaseObject() { LIMITED_METHOD_CONTRACT; } - ~AssemblyLoadContextBaseObject() { LIMITED_METHOD_CONTRACT; } - - public: - INT_PTR GetNativeAssemblyBinder() { LIMITED_METHOD_CONTRACT; return _nativeAssemblyLoadContext; } -}; -#if defined(TARGET_X86) && !defined(TARGET_UNIX) -#include "poppack.h" -#endif // defined(TARGET_X86) && !defined(TARGET_UNIX) - -struct NativeAssemblyNameParts -{ - PCWSTR _pName; - UINT16 _major, _minor, _build, _revision; - PCWSTR _pCultureName; - BYTE* _pPublicKeyOrToken; - int _cbPublicKeyOrToken; - DWORD _flags; -}; - -// AssemblyNameBaseObject -// This class is the base class for assembly names -// -class AssemblyNameBaseObject : public Object -{ - // Dummy definition -}; - -class WeakReferenceObject : public Object -{ -public: - uintptr_t m_taggedHandle; -}; - - -typedef DPTR(class ContinuationObject) PTR_ContinuationObject; -class ContinuationObject; - -#ifdef USE_CHECKED_OBJECTREFS - -typedef REF REFLECTMODULEBASEREF; - -typedef REF REFLECTCLASSBASEREF; - -typedef REF REFLECTMETHODREF; - -typedef REF REFLECTFIELDREF; - -typedef REF THREADBASEREF; - -typedef REF ASSEMBLYREF; - -typedef REF ASSEMBLYLOADCONTEXTREF; - -typedef REF ASSEMBLYNAMEREF; - -typedef REF THREADBASEREF; - -typedef REF CONTINUATIONREF; - -inline ARG_SLOT ObjToArgSlot(OBJECTREF objRef) -{ - LIMITED_METHOD_CONTRACT; - LPVOID v; - v = OBJECTREFToObject(objRef); - return (ARG_SLOT)(SIZE_T)v; -} - -inline OBJECTREF ArgSlotToObj(ARG_SLOT i) -{ - LIMITED_METHOD_CONTRACT; - LPVOID v; - v = (LPVOID)(SIZE_T)i; - return ObjectToOBJECTREF ((Object*)v); -} - -inline ARG_SLOT StringToArgSlot(STRINGREF sr) -{ - LIMITED_METHOD_CONTRACT; - LPVOID v; - v = OBJECTREFToObject(sr); - return (ARG_SLOT)(SIZE_T)v; -} - -inline STRINGREF ArgSlotToString(ARG_SLOT s) -{ - LIMITED_METHOD_CONTRACT; - LPVOID v; - v = (LPVOID)(SIZE_T)s; - return ObjectToSTRINGREF ((StringObject*)v); -} - -#else // USE_CHECKED_OBJECTREFS - -typedef PTR_ReflectModuleBaseObject REFLECTMODULEBASEREF; -typedef PTR_ReflectClassBaseObject REFLECTCLASSBASEREF; -typedef PTR_ReflectMethodObject REFLECTMETHODREF; -typedef PTR_ReflectFieldObject REFLECTFIELDREF; -typedef PTR_ThreadBaseObject THREADBASEREF; -typedef PTR_AssemblyBaseObject ASSEMBLYREF; -typedef PTR_AssemblyLoadContextBaseObject ASSEMBLYLOADCONTEXTREF; -typedef PTR_AssemblyNameBaseObject ASSEMBLYNAMEREF; -typedef PTR_ThreadBaseObject THREADBASEREF; -typedef PTR_ContinuationObject CONTINUATIONREF; - -#define ObjToArgSlot(objref) ((ARG_SLOT)(SIZE_T)(objref)) -#define ArgSlotToObj(s) ((OBJECTREF)(SIZE_T)(s)) - -#define StringToArgSlot(objref) ((ARG_SLOT)(SIZE_T)(objref)) -#define ArgSlotToString(s) ((STRINGREF)(SIZE_T)(s)) - -#endif //USE_CHECKED_OBJECTREFS - -#define PtrToArgSlot(ptr) ((ARG_SLOT)(SIZE_T)(ptr)) -#define ArgSlotToPtr(s) ((LPVOID)(SIZE_T)(s)) - -#define BoolToArgSlot(b) ((ARG_SLOT)(CLR_BOOL)(!!(b))) -#define ArgSlotToBool(s) ((BOOL)(s)) - -STRINGREF AllocateString(SString sstr); - -#ifdef FEATURE_COMINTEROP - -//------------------------------------------------------------- -// class ComObject, Exposed class __ComObject -// -// -//------------------------------------------------------------- -class ComObject : public Object -{ - friend class CoreLibBinder; - -protected: - - ComObject() - {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly - ~ComObject(){LIMITED_METHOD_CONTRACT;}; - -public: - OBJECTREF m_ObjectToDataMap; - - //-------------------------------------------------------------------- - // SupportsInterface - static BOOL SupportsInterface(OBJECTREF oref, MethodTable* pIntfTable); - - //-------------------------------------------------------------------- - // SupportsInterface - static void ThrowInvalidCastException(OBJECTREF *pObj, MethodTable* pCastToMT); - - //----------------------------------------------------------------- - // GetComIPFromRCW - static IUnknown* GetComIPFromRCW(OBJECTREF *pObj, MethodTable* pIntfTable); - - //----------------------------------------------------------------- - // GetComIPFromRCWThrowing - static IUnknown* GetComIPFromRCWThrowing(OBJECTREF *pObj, MethodTable* pIntfTable); - - //----------------------------------------------------------- - // create an empty ComObjectRef - static OBJECTREF CreateComObjectRef(MethodTable* pMT); - - //----------------------------------------------------------- - // Release all the data associated with the __ComObject. - static void ReleaseAllData(OBJECTREF oref); -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF COMOBJECTREF; -#else -typedef ComObject* COMOBJECTREF; -#endif - - -//------------------------------------------------------------- -// class UnknownWrapper, Exposed class UnknownWrapper -// -// -//------------------------------------------------------------- -class UnknownWrapper : public Object -{ -protected: - - UnknownWrapper(UnknownWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. - UnknownWrapper() {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly - ~UnknownWrapper() {LIMITED_METHOD_CONTRACT;}; - - OBJECTREF m_WrappedObject; - -public: - OBJECTREF GetWrappedObject() - { - LIMITED_METHOD_CONTRACT; - return m_WrappedObject; - } - - void SetWrappedObject(OBJECTREF pWrappedObject) - { - LIMITED_METHOD_CONTRACT; - m_WrappedObject = pWrappedObject; - } -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF UNKNOWNWRAPPEROBJECTREF; -#else -typedef UnknownWrapper* UNKNOWNWRAPPEROBJECTREF; -#endif - - -//------------------------------------------------------------- -// class DispatchWrapper, Exposed class DispatchWrapper -// -// -//------------------------------------------------------------- -class DispatchWrapper : public Object -{ -protected: - - DispatchWrapper(DispatchWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. - DispatchWrapper() {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly - ~DispatchWrapper() {LIMITED_METHOD_CONTRACT;}; - - OBJECTREF m_WrappedObject; - -public: - OBJECTREF GetWrappedObject() - { - LIMITED_METHOD_CONTRACT; - return m_WrappedObject; - } - - void SetWrappedObject(OBJECTREF pWrappedObject) - { - LIMITED_METHOD_CONTRACT; - m_WrappedObject = pWrappedObject; - } -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF DISPATCHWRAPPEROBJECTREF; -#else -typedef DispatchWrapper* DISPATCHWRAPPEROBJECTREF; -#endif - - -//------------------------------------------------------------- -// class VariantWrapper, Exposed class VARIANTWRAPPEROBJECTREF -// -// -//------------------------------------------------------------- -class VariantWrapper : public Object -{ -protected: - - VariantWrapper(VariantWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. - VariantWrapper() {LIMITED_METHOD_CONTRACT}; // don't instantiate this class directly - ~VariantWrapper() {LIMITED_METHOD_CONTRACT}; - - OBJECTREF m_WrappedObject; - -public: - OBJECTREF GetWrappedObject() - { - LIMITED_METHOD_CONTRACT; - return m_WrappedObject; - } - - void SetWrappedObject(OBJECTREF pWrappedObject) - { - LIMITED_METHOD_CONTRACT; - m_WrappedObject = pWrappedObject; - } -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF VARIANTWRAPPEROBJECTREF; -#else -typedef VariantWrapper* VARIANTWRAPPEROBJECTREF; -#endif - - -//------------------------------------------------------------- -// class ErrorWrapper, Exposed class ErrorWrapper -// -// -//------------------------------------------------------------- -class ErrorWrapper : public Object -{ -protected: - - ErrorWrapper(ErrorWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. - ErrorWrapper() {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly - ~ErrorWrapper() {LIMITED_METHOD_CONTRACT;}; - - INT32 m_ErrorCode; - -public: - INT32 GetErrorCode() - { - LIMITED_METHOD_CONTRACT; - return m_ErrorCode; - } - - void SetErrorCode(int ErrorCode) - { - LIMITED_METHOD_CONTRACT; - m_ErrorCode = ErrorCode; - } -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF ERRORWRAPPEROBJECTREF; -#else -typedef ErrorWrapper* ERRORWRAPPEROBJECTREF; -#endif - - -//------------------------------------------------------------- -// class CurrencyWrapper, Exposed class CurrencyWrapper -// -// -//------------------------------------------------------------- - -// Keep this in sync with code:MethodTableBuilder.CheckForSystemTypes where -// alignment requirement of the managed System.Decimal structure is computed. -#if !defined(ALIGN_ACCESS) && !defined(FEATURE_64BIT_ALIGNMENT) -#include -#endif // !ALIGN_ACCESS && !FEATURE_64BIT_ALIGNMENT - -class CurrencyWrapper : public Object -{ -protected: - - CurrencyWrapper(CurrencyWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. - CurrencyWrapper() {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly - ~CurrencyWrapper() {LIMITED_METHOD_CONTRACT;}; - - DECIMAL m_WrappedObject; - -public: - DECIMAL GetWrappedObject() - { - LIMITED_METHOD_CONTRACT; - return m_WrappedObject; - } - - void SetWrappedObject(DECIMAL WrappedObj) - { - LIMITED_METHOD_CONTRACT; - m_WrappedObject = WrappedObj; - } -}; - -#if !defined(ALIGN_ACCESS) && !defined(FEATURE_64BIT_ALIGNMENT) -#include -#endif // !ALIGN_ACCESS && !FEATURE_64BIT_ALIGNMENT - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF CURRENCYWRAPPEROBJECTREF; -#else -typedef CurrencyWrapper* CURRENCYWRAPPEROBJECTREF; -#endif - -//------------------------------------------------------------- -// class BStrWrapper, Exposed class BSTRWRAPPEROBJECTREF -// -// -//------------------------------------------------------------- -class BStrWrapper : public Object -{ -protected: - - BStrWrapper(BStrWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. - BStrWrapper() {LIMITED_METHOD_CONTRACT}; // don't instantiate this class directly - ~BStrWrapper() {LIMITED_METHOD_CONTRACT}; - - STRINGREF m_WrappedObject; - -public: - STRINGREF GetWrappedObject() - { - LIMITED_METHOD_CONTRACT; - return m_WrappedObject; - } - - void SetWrappedObject(STRINGREF pWrappedObject) - { - LIMITED_METHOD_CONTRACT; - m_WrappedObject = pWrappedObject; - } -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF BSTRWRAPPEROBJECTREF; -#else -typedef BStrWrapper* BSTRWRAPPEROBJECTREF; -#endif - -#endif // FEATURE_COMINTEROP - - -// This class corresponds to System.MulticastDelegate on the managed side. -class DelegateObject : public Object -{ - friend class CheckAsmOffsets; - friend class CoreLibBinder; - friend struct ::cdac_data; - -public: - BOOL IsWrapperDelegate() { LIMITED_METHOD_CONTRACT; return _methodPtrAux == 0; } - - OBJECTREF GetTarget() { LIMITED_METHOD_CONTRACT; return _target; } - void SetTarget(OBJECTREF target) { WRAPPER_NO_CONTRACT; SetObjectReference(&_target, target); } - static int GetOffsetOfTarget() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _target); } - - MethodDesc* GetMethodDesc() { LIMITED_METHOD_CONTRACT; return _methodDesc; } - void SetMethodDesc(MethodDesc* methodDesc) { LIMITED_METHOD_CONTRACT; _methodDesc = methodDesc; } - static int GetOffsetOfMethodDesc() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodDesc); } - - PCODE GetMethodPtr() { LIMITED_METHOD_CONTRACT; return _methodPtr; } - void SetMethodPtr(PCODE methodPtr) { LIMITED_METHOD_CONTRACT; _methodPtr = methodPtr; } - static int GetOffsetOfMethodPtr() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtr); } - - PCODE GetMethodPtrAux() { LIMITED_METHOD_CONTRACT; return _methodPtrAux; } - void SetMethodPtrAux(PCODE methodPtrAux) { LIMITED_METHOD_CONTRACT; _methodPtrAux = methodPtrAux; } - static int GetOffsetOfMethodPtrAux() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtrAux); } - - OBJECTREF GetInvocationList() { LIMITED_METHOD_CONTRACT; return _invocationList; } - void SetInvocationList(OBJECTREF invocationList) { WRAPPER_NO_CONTRACT; SetObjectReference(&_invocationList, invocationList); } - static int GetOffsetOfInvocationList() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _invocationList); } - - INT_PTR GetInvocationCount() { LIMITED_METHOD_CONTRACT; return _invocationCount; } - void SetInvocationCount(INT_PTR invocationCount) { LIMITED_METHOD_CONTRACT; _invocationCount = invocationCount; } - static int GetOffsetOfInvocationCount() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _invocationCount); } - - // README: - // If you modify the order of these fields, make sure to update the definition in - // BCL for this object. -private: - // System.Delegate -<<<<<<< HEAD - OBJECTREF _target; - MethodDesc* _methodDesc; -======= - OBJECTREF _methodBase; - OBJECTREF _target; ->>>>>>> upstream/main - PCODE _methodPtr; - PCODE _methodPtrAux; - // System.MulticastDelegate - OBJECTREF _invocationList; - INT_PTR _invocationCount; -}; - -<<<<<<< HEAD -#define OFFSETOF__DelegateObject__target OBJECT_SIZE /* m_pMethTab */ -#define OFFSETOF__DelegateObject__methodDesc (OFFSETOF__DelegateObject__target + TARGET_POINTER_SIZE /* _target */) -#define OFFSETOF__DelegateObject__methodPtr (OFFSETOF__DelegateObject__methodDesc + TARGET_POINTER_SIZE /* _methodDesc */) -======= -#define OFFSETOF__DelegateObject__target (OBJECT_SIZE /* m_pMethTab */ + TARGET_POINTER_SIZE /* _methodBase */) -#define OFFSETOF__DelegateObject__methodPtr (OFFSETOF__DelegateObject__target + TARGET_POINTER_SIZE /* _target */) ->>>>>>> upstream/main -#define OFFSETOF__DelegateObject__methodPtrAux (OFFSETOF__DelegateObject__methodPtr + TARGET_POINTER_SIZE /* _methodPtr */) - -template<> -struct cdac_data -{ - static constexpr size_t Target = offsetof(DelegateObject, _target); - static constexpr size_t MethodPtr = offsetof(DelegateObject, _methodPtr); - static constexpr size_t MethodPtrAux = offsetof(DelegateObject, _methodPtrAux); - static constexpr size_t InvocationCount = offsetof(DelegateObject, _invocationCount); -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF DELEGATEREF; -#else // USE_CHECKED_OBJECTREFS -typedef DelegateObject* DELEGATEREF; -#endif // USE_CHECKED_OBJECTREFS - - -struct StackTraceElement; -class ClrDataAccess; - - -typedef DPTR(StackTraceElement) PTR_StackTraceElement; - -class StackTraceArray -{ - struct ArrayHeader - { - uint32_t m_size; - uint32_t m_keepAliveItemsCount; - Thread * m_thread; - }; - - typedef DPTR(ArrayHeader) PTR_ArrayHeader; - -public: - StackTraceArray() - : m_array(static_cast(NULL)) - { - WRAPPER_NO_CONTRACT; - } - - StackTraceArray(I1ARRAYREF array) - : m_array(array) - { - LIMITED_METHOD_CONTRACT; - } - - void Set(I1ARRAYREF array) - { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - } - CONTRACTL_END; - SUPPORTS_DAC; - m_array = array; - } - - uint32_t Size() const - { - WRAPPER_NO_CONTRACT; - if (!m_array) - return 0; - else - return GetSize(); - } - - StackTraceElement const & operator[](size_t index) const; - StackTraceElement & operator[](size_t index); - - size_t Capacity() const; - void Append(StackTraceElement const * element); - void Allocate(size_t size); - - I1ARRAYREF Get() const - { - LIMITED_METHOD_DAC_CONTRACT; - return m_array; - } - - uint32_t CopyDataFrom(StackTraceArray const & src); - - Thread * GetObjectThread() const - { - WRAPPER_NO_CONTRACT; - return GetHeader()->m_thread; - } - - void SetSize(uint32_t size) - { - WRAPPER_NO_CONTRACT; - VolatileStore(&GetHeader()->m_size, size); - } - - void SetKeepAliveItemsCount(uint32_t count) - { - WRAPPER_NO_CONTRACT; - VolatileStore(&GetHeader()->m_keepAliveItemsCount, count); - } - - uint32_t GetKeepAliveItemsCount() const - { - WRAPPER_NO_CONTRACT; - return VolatileLoad(&GetHeader()->m_keepAliveItemsCount); - } - - // Compute the number of methods in the stack trace that can be collected. We need to store keepAlive - // objects (Resolver / LoaderAllocator) for these methods. - uint32_t ComputeKeepAliveItemsCount(); - - void MarkAsFrozen() - { - if (m_array != NULL) - { - GetHeader()->m_thread = (Thread *)(size_t)1; - } - } - - bool IsFrozen() const - { - return GetHeader()->m_thread == (Thread *)(size_t)1; - } - -private: - StackTraceArray(StackTraceArray const & rhs) = delete; - - StackTraceArray & operator=(StackTraceArray const & rhs) = delete; - - void CheckState() const; - - uint32_t GetSize() const - { - WRAPPER_NO_CONTRACT; - return VolatileLoad(&GetHeader()->m_size); - } - - void SetObjectThread() - { - WRAPPER_NO_CONTRACT; - GetHeader()->m_thread = GetThread(); - } - - StackTraceElement const * GetData() const - { - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - return dac_cast(GetRaw() + sizeof(ArrayHeader)); - } - - PTR_StackTraceElement GetData() - { - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - return dac_cast(GetRaw() + sizeof(ArrayHeader)); - } - - CLR_I1 const * GetRaw() const - { - WRAPPER_NO_CONTRACT; - _ASSERTE(!!m_array); - - return m_array->GetDirectPointerToNonObjectElements(); - } - - PTR_INT8 GetRaw() - { - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - _ASSERTE(!!m_array); - - return dac_cast(m_array->GetDirectPointerToNonObjectElements()); - } - - ArrayHeader const * GetHeader() const - { - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - return dac_cast(GetRaw()); - } - - PTR_ArrayHeader GetHeader() - { - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - return dac_cast(GetRaw()); - } - - void SetArray(I1ARRAYREF const & arr) - { - LIMITED_METHOD_CONTRACT; - m_array = arr; - } - -private: - // put only things here that can be protected with GCPROTECT - I1ARRAYREF m_array; - - friend struct ::cdac_data; -}; - -template<> -struct cdac_data -{ - static constexpr size_t Size = offsetof(StackTraceArray::ArrayHeader, m_size); - static constexpr size_t HeaderSize = sizeof(StackTraceArray::ArrayHeader); -}; - -#ifdef FEATURE_COLLECTIBLE_TYPES - -class LoaderAllocatorScoutObject : public Object -{ - friend class CoreLibBinder; - friend class LoaderAllocatorObject; - -protected: - LoaderAllocator * m_nativeLoaderAllocator; -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF LOADERALLOCATORSCOUTREF; -#else // USE_CHECKED_OBJECTREFS -typedef LoaderAllocatorScoutObject* LOADERALLOCATORSCOUTREF; -#endif // USE_CHECKED_OBJECTREFS - -class LoaderAllocatorObject : public Object -{ - friend class CoreLibBinder; - -public: - // All uses of this api must be safe lock-free reads used only for reading from the handle table - // The normal GetHandleTable can only be called while holding the handle table lock, but - // this is for use in lock-free scenarios - PTRARRAYREF DangerousGetHandleTable() - { - LIMITED_METHOD_DAC_CONTRACT; - return (PTRARRAYREF)ObjectToOBJECTREF(VolatileLoadWithoutBarrier((Object**)&m_pSlots)); - } - -#ifndef DACCESS_COMPILE - PTRARRAYREF GetHandleTable(); - void SetHandleTable(PTRARRAYREF handleTable); - INT32 GetSlotsUsed(); - void SetSlotsUsed(INT32 newSlotsUsed); -#endif // DACCESS_COMPILE - - void SetNativeLoaderAllocator(LoaderAllocator * pLoaderAllocator) - { - LIMITED_METHOD_CONTRACT; - m_pLoaderAllocatorScout->m_nativeLoaderAllocator = pLoaderAllocator; - } - - // README: - // If you modify the order of these fields, make sure to update the definition in - // BCL for this object. -protected: - LOADERALLOCATORSCOUTREF m_pLoaderAllocatorScout; - OBJECTREF m_pSlots; - INT32 m_slotsUsed; - OBJECTREF m_methodInstantiationsTable; -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF LOADERALLOCATORREF; -#else // USE_CHECKED_OBJECTREFS -typedef DPTR(LoaderAllocatorObject) PTR_LoaderAllocatorObject; -typedef PTR_LoaderAllocatorObject LOADERALLOCATORREF; -#endif // USE_CHECKED_OBJECTREFS - -#endif // FEATURE_COLLECTIBLE_TYPES - -typedef DPTR(class GenericCacheStruct) PTR_GenericCacheStruct; -class GenericCacheStruct -{ - friend class CoreLibBinder; - public: - - ARRAYBASEREF GetTable() const - { - LIMITED_METHOD_CONTRACT; - return _table; - } - - int32_t CacheElementCount() const - { - LIMITED_METHOD_CONTRACT; - return GetTable()->GetNumComponents() - 1; - } - - ARRAYBASEREF GetSentinelTable() const - { - LIMITED_METHOD_CONTRACT; - return _sentinelTable; - } - - void SetTable(ARRAYBASEREF table) - { - WRAPPER_NO_CONTRACT; - SetObjectReference((OBJECTREF*)&_table, (OBJECTREF)table); - } - - void SetLastFlushSize(int32_t lastFlushSize) - { - LIMITED_METHOD_CONTRACT; - _lastFlushSize = lastFlushSize; - } - - int32_t GetInitialCacheSize() const - { - LIMITED_METHOD_CONTRACT; - return _initialCacheSize; - } - -#ifdef DEBUG - static void ValidateLayout(MethodTable* pMTOfInstantiation); -#endif - - private: - // README: - // If you modify the order of these fields, make sure to update the definition in - // BCL for this object. - - ARRAYBASEREF _table; - ARRAYBASEREF _sentinelTable; - int32_t _lastFlushSize; - int32_t _initialCacheSize; - int32_t _maxCacheSize; -}; - -class ContinuationObject : public Object -{ - friend class CoreLibBinder; - - public: - CorInfoContinuationFlags GetFlags() const - { - LIMITED_METHOD_CONTRACT; - return (CorInfoContinuationFlags)Flags; - } - - void SetFlags(CorInfoContinuationFlags flags) - { - LIMITED_METHOD_CONTRACT; - Flags = (int32_t)flags; - } - - void SetResumeInfo(void* resumeInfo) - { - LIMITED_METHOD_CONTRACT; - ResumeInfo = resumeInfo; - } - - void* GetResumeInfo() const - { - LIMITED_METHOD_CONTRACT; - return ResumeInfo; - } - - void SetState(int32_t state) - { - LIMITED_METHOD_CONTRACT; - State = state; - } - - int32_t GetState() const - { - LIMITED_METHOD_CONTRACT; - return State; - } - - PTR_BYTE GetResultStorage() - { - LIMITED_METHOD_CONTRACT; - - uint32_t mask = (1u << CORINFO_CONTINUATION_RESULT_INDEX_NUM_BITS) - 1; - uint32_t index = ((uint32_t)Flags >> CORINFO_CONTINUATION_RESULT_INDEX_FIRST_BIT) & mask; - _ASSERTE(index != 0); - - uint32_t offset = OFFSETOF__CORINFO_Continuation__data + (index - 1) * TARGET_POINTER_SIZE; - PTR_BYTE dataAddress = dac_cast((dac_cast(this) + offset)); - return dataAddress; - } - - PTR_OBJECTREF GetExceptionObjectStorageOrNull() - { - LIMITED_METHOD_CONTRACT; - - uint32_t mask = (1u << CORINFO_CONTINUATION_EXCEPTION_INDEX_NUM_BITS) - 1; - uint32_t index = ((uint32_t)Flags >> CORINFO_CONTINUATION_EXCEPTION_INDEX_FIRST_BIT) & mask; - if (index == 0) - return NULL; - - uint32_t offset = OFFSETOF__CORINFO_Continuation__data + (index - 1) * TARGET_POINTER_SIZE; - PTR_BYTE dataAddress = dac_cast(dac_cast(this) + offset); - return dac_cast(dataAddress); - } - - PTR_OBJECTREF GetExecutionContextObjectStorageOrNull() - { - LIMITED_METHOD_CONTRACT; - - uint32_t mask = (1u << CORINFO_CONTINUATION_EXECUTION_CONTEXT_INDEX_NUM_BITS) - 1; - uint32_t index = ((uint32_t)Flags >> CORINFO_CONTINUATION_EXECUTION_CONTEXT_INDEX_FIRST_BIT) & mask; - if (index == 0) - return NULL; - - uint32_t offset = OFFSETOF__CORINFO_Continuation__data + (index - 1) * TARGET_POINTER_SIZE; - PTR_BYTE address = dac_cast(dac_cast(this) + offset); - return dac_cast(address); - } - - PTR_OBJECTREF GetContinuationContextObjectStorageOrNull() - { - LIMITED_METHOD_CONTRACT; - - uint32_t mask = (1u << CORINFO_CONTINUATION_CONTEXT_INDEX_NUM_BITS) - 1; - uint32_t index = ((uint32_t)Flags >> CORINFO_CONTINUATION_CONTEXT_INDEX_FIRST_BIT) & mask; - if (index == 0) - return NULL; - - uint32_t offset = OFFSETOF__CORINFO_Continuation__data + (index - 1) * TARGET_POINTER_SIZE; - PTR_BYTE address = dac_cast(dac_cast(this) + offset); - return dac_cast(address); - } - -#ifndef DACCESS_COMPILE - int32_t* GetFlagsAddress() - { - LIMITED_METHOD_CONTRACT; - return (int32_t*)&Flags; - } -#endif // DACCESS_COMPILE - -private: - // README: - // If you modify the order of these fields, make sure to update the definition in - // BCL for this object. - - CONTINUATIONREF Next; - void* ResumeInfo; - int32_t Flags; - int32_t State; -}; - -// This class corresponds to Exception on the managed side. -typedef DPTR(class ExceptionObject) PTR_ExceptionObject; -#include "pshpack4.h" -class ExceptionObject : public Object -{ - friend class CoreLibBinder; - -public: - void SetHResult(HRESULT hr) - { - LIMITED_METHOD_CONTRACT; - _HResult = hr; - } - - HRESULT GetHResult() - { - LIMITED_METHOD_CONTRACT; - return _HResult; - } - - void SetXCode(DWORD code) - { - LIMITED_METHOD_CONTRACT; - _xcode = code; - } - - DWORD GetXCode() - { - LIMITED_METHOD_CONTRACT; - return _xcode; - } - - void SetXPtrs(void* xptrs) - { - LIMITED_METHOD_CONTRACT; - _xptrs = xptrs; - } - - void* GetXPtrs() - { - LIMITED_METHOD_CONTRACT; - return _xptrs; - } - - void SetStackTrace(OBJECTREF stackTrace); - - void GetStackTrace(StackTraceArray & stackTrace, PTRARRAYREF * outKeepaliveArray = NULL) const - { -#ifdef DACCESS_COMPILE - return GetStackTrace(stackTrace, outKeepaliveArray, NULL); -#else - return GetStackTrace(stackTrace, outKeepaliveArray, GetThread()); -#endif // DACCESS_COMPILE - } - -private: - static void GetStackTraceClone(StackTraceArray & stackTrace, PTRARRAYREF * outKeepAliveArray); -public: - void GetStackTrace(StackTraceArray & stackTrace, PTRARRAYREF * outKeepaliveArray, Thread *pCurrentThread) const; - - static void GetStackTraceParts(OBJECTREF stackTraceObj, StackTraceArray & stackTrace, PTRARRAYREF * outKeepaliveArray); - - OBJECTREF GetStackTraceArrayObject() const - { - LIMITED_METHOD_DAC_CONTRACT; - return _stackTrace; - } - - void SetInnerException(OBJECTREF innerException) - { - WRAPPER_NO_CONTRACT; - SetObjectReference((OBJECTREF*)&_innerException, (OBJECTREF)innerException); - } - - OBJECTREF GetInnerException() - { - LIMITED_METHOD_DAC_CONTRACT; - return VolatileLoadWithoutBarrierOBJECTREF(&_innerException); - } - - // Returns the innermost exception object - equivalent of the - // managed System.Exception.GetBaseException method. - OBJECTREF GetBaseException() - { - LIMITED_METHOD_CONTRACT; - - // Loop and get the innermost exception object - OBJECTREF oInnerMostException = NULL; - OBJECTREF oCurrent = NULL; - - oCurrent = GetInnerException(); - while(oCurrent != NULL) - { - oInnerMostException = oCurrent; - oCurrent = ((ExceptionObject*)(Object *)OBJECTREFToObject(oCurrent))->GetInnerException(); - } - - // return the innermost exception - return oInnerMostException; - } - - void SetMessage(STRINGREF message) - { - WRAPPER_NO_CONTRACT; - SetObjectReference((OBJECTREF*)&_message, (OBJECTREF)message); - } - - STRINGREF GetMessage() - { - LIMITED_METHOD_DAC_CONTRACT; - return _message; - } - - void SetStackTraceString(STRINGREF stackTraceString) - { - WRAPPER_NO_CONTRACT; - SetObjectReference((OBJECTREF*)&_stackTraceString, (OBJECTREF)stackTraceString); - } - - STRINGREF GetStackTraceString() - { - LIMITED_METHOD_DAC_CONTRACT; - return _stackTraceString; - } - - STRINGREF GetRemoteStackTraceString() - { - LIMITED_METHOD_DAC_CONTRACT; - return (STRINGREF)VolatileLoadWithoutBarrierOBJECTREF(&_remoteStackTraceString); - } - - void SetHelpURL(STRINGREF helpURL) - { - WRAPPER_NO_CONTRACT; - SetObjectReference((OBJECTREF*)&_helpURL, (OBJECTREF)helpURL); - } - - void SetSource(STRINGREF source) - { - WRAPPER_NO_CONTRACT; - SetObjectReference((OBJECTREF*)&_source, (OBJECTREF)source); - } - - void ClearStackTraceForThrow() - { - WRAPPER_NO_CONTRACT; - SetObjectReference((OBJECTREF*)&_remoteStackTraceString, NULL); - SetObjectReference((OBJECTREF*)&_stackTrace, NULL); - SetObjectReference((OBJECTREF*)&_stackTraceString, NULL); - } - - void ClearStackTracePreservingRemoteStackTrace() - { - WRAPPER_NO_CONTRACT; - SetObjectReference((OBJECTREF*)&_stackTrace, NULL); - SetObjectReference((OBJECTREF*)&_stackTraceString, NULL); - } - - // This method will set the reference to the array - // containing the watson bucket information (in byte[] form). - void SetWatsonBucketReference(OBJECTREF oWatsonBucketArray) - { - WRAPPER_NO_CONTRACT; - SetObjectReference((OBJECTREF*)&_watsonBuckets, (OBJECTREF)oWatsonBucketArray); - } - - // This method will return the reference to the array - // containing the watson buckets - U1ARRAYREF GetWatsonBucketReference() - { - LIMITED_METHOD_CONTRACT; - return (U1ARRAYREF)VolatileLoadWithoutBarrierOBJECTREF(&_watsonBuckets); - } - - // This method will return a BOOL to indicate if the - // watson buckets are present or not. - BOOL AreWatsonBucketsPresent() - { - LIMITED_METHOD_CONTRACT; - return (GetWatsonBucketReference() != NULL)?TRUE:FALSE; - } - - // This method will save the IP to be used for watson bucketing. - void SetIPForWatsonBuckets(UINT_PTR ip) - { - LIMITED_METHOD_CONTRACT; - - _ipForWatsonBuckets = ip; - } - - // This method will return a BOOL to indicate if Watson bucketing IP - // is present (or not). - BOOL IsIPForWatsonBucketsPresent() - { - LIMITED_METHOD_CONTRACT; - - return (_ipForWatsonBuckets != 0); - } - - // This method returns the IP for Watson Buckets. - UINT_PTR GetIPForWatsonBuckets() - { - LIMITED_METHOD_CONTRACT; - - return VolatileLoadWithoutBarrier(&_ipForWatsonBuckets); - } - - // README: - // If you modify the order of these fields, make sure to update the definition in - // BCL for this object. -private: - OBJECTREF _exceptionMethod; //Needed for serialization. - STRINGREF _message; - OBJECTREF _data; - OBJECTREF _innerException; - STRINGREF _helpURL; - OBJECTREF _stackTrace; - U1ARRAYREF _watsonBuckets; - STRINGREF _stackTraceString; //Needed for serialization. - STRINGREF _remoteStackTraceString; - STRINGREF _source; // Mainly used by VB. - - UINT_PTR _ipForWatsonBuckets; // Contains the IP of exception for watson bucketing - void* _xptrs; - INT32 _xcode; - INT32 _HResult; - - friend struct ::cdac_data; -}; - -template<> -struct cdac_data -{ - static constexpr size_t _message = offsetof(ExceptionObject, _message); - static constexpr size_t _innerException = offsetof(ExceptionObject, _innerException); - static constexpr size_t _stackTrace = offsetof(ExceptionObject, _stackTrace); - static constexpr size_t _watsonBuckets = offsetof(ExceptionObject, _watsonBuckets); - static constexpr size_t _stackTraceString = offsetof(ExceptionObject, _stackTraceString); - static constexpr size_t _remoteStackTraceString = offsetof(ExceptionObject, _remoteStackTraceString); - static constexpr size_t _HResult = offsetof(ExceptionObject, _HResult); - static constexpr size_t _xcode = offsetof(ExceptionObject, _xcode); -}; - -#include "poppack.h" - -//=============================================================================== -// #NullableFeature -// #NullableArchitecture -// -// In a nutshell it is counterintuitive to have a boxed Nullable, since a boxed -// object already has a representation for null (the null pointer), and having -// multiple representations for the 'not present' value just causes grief. Thus the -// feature is build make Nullable box to a boxed (not boxed). -// -// We want to do this in a way that does not impact the perf of the runtime in the -// non-nullable case. -// -// To do this we need to -// * Make a new helper for the Unbox case (see code:JIT_Unbox_Nullable) -// * Plumb the JIT to ask for what kind of Boxing helper is needed -// (see code:CEEInfo.getBoxHelper, code:CEEInfo.getUnBoxHelper -// * change all the places in the CLR where we box or unbox by hand, and force -// them to use code:MethodTable.Box, and code:MethodTable.Unbox which in -// turn call code:Nullable.Box and code:Nullable.UnBox, most of these -// are in reflection, and remoting (passing and returning value types). -// -// #NullableVerification -// -// Sadly, the IL Verifier also needs to know about this change. Basically the 'box' -// instruction returns a boxed(T) (not a boxed(Nullable)) for the purposes of -// verfication. The JIT finds out what box returns by calling back to the EE with -// the code:CEEInfo.getTypeForBox API. -// -// #NullableDebugging -// -// Sadly, because the debugger also does its own boxing 'by hand' for expression -// evaluation inside visual studio, it measn that debuggers also need to be aware -// of the fact that Nullable boxes to a boxed. It is the responcibility of -// debuggers to follow this convention (which is why this is sad). -// - -//=============================================================================== -// Nullable represents the managed generic value type Nullable -// -// The runtime has special logic for this value class. When it is boxed -// it becomes either null or a boxed T. Similarly a boxed T can be unboxed -// either as a T (as normal), or as a Nullable -// -// See code:Nullable#NullableArchitecture for more. -// -class Nullable { - Nullable(); // This is purposefully undefined. Do not make instances - // of this class. -public: - static void CheckFieldOffsets(TypeHandle nullableType); - static BOOL IsNullableType(TypeHandle nullableType); - static BOOL IsNullableForType(TypeHandle nullableType, MethodTable* paramMT); - - static OBJECTREF Box(void* src, MethodTable* nullable); - static BOOL UnBox(void* dest, OBJECTREF boxedVal, MethodTable* destMT); - static void UnBoxNoCheck(void* dest, OBJECTREF boxedVal, MethodTable* destMT); - static OBJECTREF BoxedNullableNull(TypeHandle nullableType) { return NULL; } - // if 'Obj' is a true boxed nullable, return the form we want (either null or a boxed T) - static OBJECTREF NormalizeBox(OBJECTREF obj); - - static inline CLR_BOOL HasValue(void *src, MethodTable *nullableMT) - { - Nullable *nullable = (Nullable *)src; - return *(nullable->HasValueAddr(nullableMT)); - } - - static inline void *Value(void *src, MethodTable *nullableMT) - { - Nullable *nullable = (Nullable *)src; - return nullable->ValueAddr(nullableMT); - } - - static int32_t GetValueAddrOffset(MethodTable* nullableMT); - -private: - static BOOL IsNullableForTypeHelper(MethodTable* nullableMT, MethodTable* paramMT); - - CLR_BOOL* HasValueAddr(MethodTable* nullableMT); - void* ValueAddr(MethodTable* nullableMT); -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF EXCEPTIONREF; -#else // USE_CHECKED_OBJECTREFS -typedef PTR_ExceptionObject EXCEPTIONREF; -#endif // USE_CHECKED_OBJECTREFS - -#endif // _OBJECT_H_ diff --git a/src/coreclr/vm/object.h.orig b/src/coreclr/vm/object.h.orig deleted file mode 100644 index 89a1de721ae0d4..00000000000000 --- a/src/coreclr/vm/object.h.orig +++ /dev/null @@ -1,2586 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// -// OBJECT.H -// -// Definitions of a CLR Object -// - -// See code:EEStartup#TableOfContents for overview - - -#ifndef _OBJECT_H_ -#define _OBJECT_H_ - -#include "util.hpp" -#include "syncblk.h" -#include "gcdesc.h" -#include "sstring.h" -#include "daccess.h" -#include "fcall.h" - -extern "C" void __fastcall ZeroMemoryInGCHeap(void*, size_t); - -void ErectWriteBarrierForMT(MethodTable **dst, MethodTable *ref); - -/* - #ObjectModel - * CLR Internal Object Model - * - * - * Object - This is the common base part to all CLR objects - * | it contains the MethodTable pointer and the - * | sync block index, which is at a negative offset - * | - * +-- StringObject - String objects are specialized objects for string - * | storage/retrieval for higher performance (UCS-2 / UTF-16 data) - * | - * +-- ReflectClassBaseObject - The base object for the RuntimeType class - * | - * +-- ReflectMethodObject - The base object for the RuntimeMethodInfo class - * | - * +-- ReflectFieldObject - The base object for the RtFieldInfo class - * | - * +-- ArrayBase - Base portion of all arrays - * | | - * | +- I1Array - Base type SZ arrays - * | | I2Array - * | | ... - * | | - * | +- PtrArray - SZ Array of OBJECTREFs, different than base arrays because of pObjectClass - * | - * +-- AssemblyBaseObject - The base object for the class Assembly - * | - * | ... - * - * - * PLEASE NOTE THE FOLLOWING WHEN ADDING A NEW OBJECT TYPE: - * - * The size of the object in the heap must be able to be computed - * very, very quickly for GC purposes. Restrictions on the layout - * of the object guarantee this is possible. - * - * Any object that inherits from Object must be able to - * compute its complete size by using the first 4 bytes of - * the object following the Object part and constants - * reachable from the MethodTable... - * - * The formula used for this calculation is: - * MT->GetBaseSize() + ((OBJECTTYPEREF->GetSizeField() * MT->GetComponentSize()) - * - * So for Object, since this is of fixed size, the ComponentSize is 0, which makes the right side - * of the equation above equal to 0 no matter what the value of GetSizeField(), so the size is just the base size. - * - */ - -class MethodTable; -class Thread; -class Assembly; -class AssemblyNative; -class WaitHandleNative; -class ArgDestination; - -struct RCW; - -#ifdef TARGET_64BIT -#define OBJHEADER_SIZE (sizeof(DWORD) /* m_alignpad */ + sizeof(DWORD) /* m_SyncBlockValue */) -#else -#define OBJHEADER_SIZE sizeof(DWORD) /* m_SyncBlockValue */ -#endif - -#define OBJECT_SIZE TARGET_POINTER_SIZE /* m_pMethTab */ -#define OBJECT_BASESIZE (OBJHEADER_SIZE + OBJECT_SIZE) - -#ifdef TARGET_64BIT -#define ARRAYBASE_SIZE (OBJECT_SIZE /* m_pMethTab */ + sizeof(DWORD) /* m_NumComponents */ + sizeof(DWORD) /* pad */) -#else -#define ARRAYBASE_SIZE (OBJECT_SIZE /* m_pMethTab */ + sizeof(DWORD) /* m_NumComponents */) -#endif - -#define ARRAYBASE_BASESIZE (OBJHEADER_SIZE + ARRAYBASE_SIZE) - -// -// The generational GC requires that every object be at least 12 bytes -// in size. - -#define MIN_OBJECT_SIZE (2*TARGET_POINTER_SIZE + OBJHEADER_SIZE) - -#define PTRALIGNCONST (DATA_ALIGNMENT-1) - -#ifndef PtrAlign -#define PtrAlign(size) \ - (((size) + PTRALIGNCONST) & (~PTRALIGNCONST)) -#endif //!PtrAlign - -// code:Object is the representation of an managed object on the GC heap. -// -// See code:#ObjectModel for some important subclasses of code:Object -// -// The only fields mandated by all objects are -// -// * a pointer to the code:MethodTable at offset 0 -// * a pointer to a code:ObjHeader at a negative offset. This is often zero. It holds information that -// any addition information that we might need to attach to arbitrary objects. -// -class Object -{ - friend class CheckAsmOffsets; - - protected: - PTR_MethodTable m_pMethTab; - - protected: - Object() { LIMITED_METHOD_CONTRACT; }; - ~Object() { LIMITED_METHOD_CONTRACT; }; - - public: - MethodTable *RawGetMethodTable() const - { - return m_pMethTab; - } - -#ifndef DACCESS_COMPILE - void RawSetMethodTable(MethodTable *pMT) - { - LIMITED_METHOD_CONTRACT; - m_pMethTab = pMT; - } - - VOID SetMethodTable(MethodTable *pMT) - { - WRAPPER_NO_CONTRACT; - RawSetMethodTable(pMT); - } - - VOID SetMethodTableForUOHObject(MethodTable *pMT) - { - WRAPPER_NO_CONTRACT; - // This function must be used if the allocation occurs on a UOH heap, and the method table might be a collectible type - ErectWriteBarrierForMT(&m_pMethTab, pMT); - } -#endif //!DACCESS_COMPILE - -#define MARKED_BIT 0x1 - - PTR_MethodTable GetMethodTable() const - { - LIMITED_METHOD_DAC_CONTRACT; - -#ifndef DACCESS_COMPILE - // We should always use GetGCSafeMethodTable() if we're running during a GC. - // If the mark bit is set then we're running during a GC - _ASSERTE((dac_cast(m_pMethTab) & MARKED_BIT) == 0); - - return m_pMethTab; -#else //DACCESS_COMPILE - - //@dbgtodo dharvey Make this a type which supports bitwise and operations - //when available - return PTR_MethodTable((dac_cast(m_pMethTab)) & (~MARKED_BIT)); -#endif //DACCESS_COMPILE - } - - DPTR(PTR_MethodTable) GetMethodTablePtr() const - { - LIMITED_METHOD_CONTRACT; - return dac_cast(PTR_HOST_MEMBER_TADDR(Object, this, m_pMethTab)); - } - - TypeHandle GetTypeHandle(); - - // Methods used to determine if an object supports a given interface. - static BOOL SupportsInterface(OBJECTREF pObj, MethodTable *pInterfaceMT); - - inline DWORD GetNumComponents(); - inline SIZE_T GetSize(); - - CGCDesc* GetSlotMap() - { - WRAPPER_NO_CONTRACT; - return( CGCDesc::GetCGCDescFromMT(GetMethodTable())); - } - - // Sync Block & Synchronization services - - // Access the ObjHeader which is at a negative offset on the object (because of - // cache lines) - PTR_ObjHeader GetHeader() - { - LIMITED_METHOD_DAC_CONTRACT; - return dac_cast(this) - 1; - } - - // Get the current address of the object (works for debug refs, too.) - PTR_BYTE GetAddress() - { - LIMITED_METHOD_DAC_CONTRACT; - return dac_cast(this); - } - -#ifdef _DEBUG - // TRUE if the header has a real SyncBlockIndex (i.e. it has an entry in the - // SyncTable, though it doesn't necessarily have an entry in the SyncBlockCache) - BOOL HasEmptySyncBlockInfo() - { - WRAPPER_NO_CONTRACT; - return GetHeader()->HasEmptySyncBlockInfo(); - } -#endif - - // retrieve or allocate a sync block for this object - SyncBlock *GetSyncBlock() - { - WRAPPER_NO_CONTRACT; - return GetHeader()->GetSyncBlock(); - } - - DWORD GetSyncBlockIndex() - { - WRAPPER_NO_CONTRACT; - return GetHeader()->GetSyncBlockIndex(); - } - - // DO NOT ADD ANY ASSERTS TO THIS METHOD. - // DO NOT USE THIS METHOD. - // Yes folks, for better or worse the debugger pokes supposed object addresses - // to try to see if objects are valid, possibly firing an AccessViolation or worse, - // and then catches the AV and reports a failure to the debug client. This makes - // the debugger slightly more robust should any corrupted object references appear - // in a session. Thus it is "correct" behaviour for this to AV when used with - // an invalid object pointer, and incorrect behaviour for it to - // assert. - BOOL ValidateObjectWithPossibleAV(); - - // Validate an object ref out of the VerifyHeap routine in the GC - void ValidateHeap(BOOL bDeep=TRUE); - - PTR_SyncBlock PassiveGetSyncBlock() - { - LIMITED_METHOD_DAC_CONTRACT; - return GetHeader()->PassiveGetSyncBlock(); - } - - static DWORD ComputeHashCode(); - static DWORD GetGlobalNewHashCode(); - - inline INT32 TryGetHashCode(); -#ifndef DACCESS_COMPILE - INT32 GetHashCodeEx(); -#endif // #ifndef DACCESS_COMPILE - - PTR_VOID UnBox(); // if it is a value class, get the pointer to the first field - - PTR_BYTE GetData(void) - { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - return dac_cast(this) + sizeof(Object); - } - - static UINT GetOffsetOfFirstField() - { - LIMITED_METHOD_CONTRACT; - return sizeof(Object); - } - - DWORD GetOffset32(DWORD dwOffset) - { - WRAPPER_NO_CONTRACT; - return * PTR_DWORD(GetData() + dwOffset); - } - - USHORT GetOffset16(DWORD dwOffset) - { - WRAPPER_NO_CONTRACT; - return * PTR_USHORT(GetData() + dwOffset); - } - - BYTE GetOffset8(DWORD dwOffset) - { - WRAPPER_NO_CONTRACT; - return * PTR_BYTE(GetData() + dwOffset); - } - - int64_t GetOffset64(DWORD dwOffset) - { - WRAPPER_NO_CONTRACT; - return (int64_t) * PTR_ULONG64(GetData() + dwOffset); - } - - void *GetPtrOffset(DWORD dwOffset) - { - WRAPPER_NO_CONTRACT; - return (void *)(TADDR)*PTR_TADDR(GetData() + dwOffset); - } - -#ifndef DACCESS_COMPILE - - void SetOffsetObjectRef(DWORD dwOffset, size_t dwValue); - - void SetOffsetPtr(DWORD dwOffset, LPVOID value) - { - WRAPPER_NO_CONTRACT; - *(LPVOID *) &GetData()[dwOffset] = value; - } - - void SetOffset32(DWORD dwOffset, DWORD dwValue) - { - WRAPPER_NO_CONTRACT; - *(DWORD *) &GetData()[dwOffset] = dwValue; - } - - void SetOffset16(DWORD dwOffset, DWORD dwValue) - { - WRAPPER_NO_CONTRACT; - *(USHORT *) &GetData()[dwOffset] = (USHORT) dwValue; - } - - void SetOffset8(DWORD dwOffset, DWORD dwValue) - { - WRAPPER_NO_CONTRACT; - *(BYTE *) &GetData()[dwOffset] = (BYTE) dwValue; - } - - void SetOffset64(DWORD dwOffset, int64_t qwValue) - { - WRAPPER_NO_CONTRACT; - *(int64_t *) &GetData()[dwOffset] = qwValue; - } - -#endif // #ifndef DACCESS_COMPILE - - VOID Validate(BOOL bDeep = TRUE, BOOL bVerifyNextHeader = TRUE, BOOL bVerifySyncBlock = TRUE); - - PTR_MethodTable GetGCSafeMethodTable() const - { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - - // lose GC marking bit and the reserved bit - // A method table pointer should always be aligned. During GC we set the least - // significant bit for marked objects, and the second to least significant - // bit is reserved. So if we want the actual MT pointer during a GC - // we must zero out the lowest 2 bits on 32-bit and 3 bits on 64-bit. -#ifdef TARGET_64BIT - return dac_cast((dac_cast(m_pMethTab)) & ~((UINT_PTR)7)); -#else - return dac_cast((dac_cast(m_pMethTab)) & ~((UINT_PTR)3)); -#endif //TARGET_64BIT - } - - // There are some cases where it is unsafe to get the type handle during a GC. - // This occurs when the type has already been unloaded as part of an in-progress appdomain shutdown. - TypeHandle GetGCSafeTypeHandleIfPossible() const; - - inline TypeHandle GetGCSafeTypeHandle() const; - -#ifdef DACCESS_COMPILE - void EnumMemoryRegions(void); -#endif - - private: - VOID ValidateInner(BOOL bDeep, BOOL bVerifyNextHeader, BOOL bVerifySyncBlock); - - friend struct ::cdac_data; -}; - -template<> -struct cdac_data -{ - static constexpr size_t m_pMethTab = offsetof(Object, m_pMethTab); -}; - -/* - * Object ref setting routines. You must use these to do - * proper write barrier support. - */ - -// SetObjectReference sets an OBJECTREF field - -void SetObjectReferenceUnchecked(OBJECTREF *dst,OBJECTREF ref); - -#ifdef _DEBUG -void EnableStressHeapHelper(); -#endif - -//Used to clear the object reference -inline void ClearObjectReference(OBJECTREF* dst) -{ - LIMITED_METHOD_CONTRACT; - *(void**)(dst) = NULL; -} - -// CopyValueClass sets a value class field - -void CopyValueClassUnchecked(void* dest, void* src, MethodTable *pMT); -void CopyValueClassArgUnchecked(ArgDestination *argDest, void* src, MethodTable *pMT, int destOffset); - -inline void InitValueClass(void *dest, MethodTable *pMT) -{ - WRAPPER_NO_CONTRACT; - ZeroMemoryInGCHeap(dest, pMT->GetNumInstanceFieldBytes()); -} - -// Initialize value class argument -void InitValueClassArg(ArgDestination *argDest, MethodTable *pMT); - -#define SetObjectReference(_d,_r) SetObjectReferenceUnchecked(_d, _r) -#define CopyValueClass(_d,_s,_m) CopyValueClassUnchecked(_d,_s,_m) -#define CopyValueClassArg(_d,_s,_m,_o) CopyValueClassArgUnchecked(_d,_s,_m,_o) - -#include - - -// There are two basic kinds of array layouts in CLR -// ELEMENT_TYPE_ARRAY - a multidimensional array with lower bounds on the dims -// ELMENNT_TYPE_SZARRAY - A zero based single dimensional array -// -// In addition the layout of an array in memory is also affected by -// whether the method table is shared (eg in the case of arrays of object refs) -// or not. In the shared case, the array has to hold the type handle of -// the element type. -// -// ArrayBase encapuslates all of these details. In theory you should never -// have to peek inside this abstraction -// -class ArrayBase : public Object -{ - friend class GCHeap; - friend class CObjectHeader; - friend class Object; - friend OBJECTREF AllocateSzArray(MethodTable *pArrayMT, INT32 length, GC_ALLOC_FLAGS flags); - friend OBJECTREF TryAllocateFrozenSzArray(MethodTable* pArrayMT, INT32 length); - friend class JIT_TrialAlloc; - friend class CheckAsmOffsets; - friend struct _DacGlobals; - -private: - // This MUST be the first field, so that it directly follows Object. This is because - // Object::GetSize() looks at m_NumComponents even though it may not be an array (the - // values is shifted out if not an array, so it's ok). - DWORD m_NumComponents; -#ifdef TARGET_64BIT - DWORD pad; -#endif // TARGET_64BIT - - SVAL_DECL(INT32, s_arrayBoundsZero); // = 0 - - // What comes after this conceputally is: - // INT32 bounds[rank]; The bounds are only present for Multidimensional arrays - // INT32 lowerBounds[rank]; Valid indexes are lowerBounds[i] <= index[i] < lowerBounds[i] + bounds[i] - -public: -#ifndef DACCESS_COMPILE - void SetNumComponents(INT32 length) - { - LIMITED_METHOD_CONTRACT; - m_NumComponents = length; - } -#endif // !DACCESS_COMPILE - - // Get the element type for the array, this works whether the element - // type is stored in the array or not - inline TypeHandle GetArrayElementTypeHandle() const; - - // Get the CorElementType for the elements in the array. Avoids creating a TypeHandle - inline CorElementType GetArrayElementType() const; - - inline unsigned GetRank() const; - - // Total element count for the array - inline DWORD GetNumComponents() const; - - // Get pointer to elements, handles any number of dimensions - PTR_BYTE GetGCSafeDataPtr() const { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; -#ifdef _DEBUG -#ifndef DACCESS_COMPILE - EnableStressHeapHelper(); -#endif -#endif - return dac_cast(this) + - GetDataPtrOffset(GetGCSafeMethodTable()); - } - - PTR_BYTE GetDataPtr() const { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; -#ifdef _DEBUG -#ifndef DACCESS_COMPILE - EnableStressHeapHelper(); -#endif -#endif - PTR_BYTE result = dac_cast(this) + - GetDataPtrOffset(GetMethodTable()); - _ASSERTE(result == GetGCSafeDataPtr()); - return result; - } - - // The component size is actually 16-bit WORD, but this method is returning SIZE_T to ensure - // that SIZE_T is used everywhere for object size computation. It is necessary to support - // objects bigger than 2GB. - SIZE_T GetComponentSize() const { - WRAPPER_NO_CONTRACT; - MethodTable * pMT; - pMT = GetMethodTable(); - _ASSERTE(pMT->HasComponentSize()); - return pMT->RawGetComponentSize(); - } - - // Note that this can be a multidimensional array of rank 1 - // (for example if we had a 1-D array with lower bounds - BOOL IsMultiDimArray() const { - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - return(GetMethodTable()->IsMultiDimArray()); - } - - // Get pointer to the beginning of the bounds (counts for each dim) - // Works for any array type - PTR_INT32 GetBoundsPtr() const { - WRAPPER_NO_CONTRACT; - MethodTable * pMT = GetMethodTable(); - if (pMT->IsMultiDimArray()) - { - return dac_cast( - dac_cast(this) + sizeof(*this)); - } - else - { - return dac_cast(PTR_HOST_MEMBER_TADDR(ArrayBase, this, - m_NumComponents)); - } - } - - // Works for any array type - PTR_INT32 GetLowerBoundsPtr() const { - WRAPPER_NO_CONTRACT; - if (IsMultiDimArray()) - { - // Lower bounds info is after total bounds info - // and total bounds info has rank elements - return GetBoundsPtr() + GetRank(); - } - else - return dac_cast(GVAL_ADDR(s_arrayBoundsZero)); - } - - static unsigned GetOffsetOfNumComponents() { - LIMITED_METHOD_CONTRACT; - return offsetof(ArrayBase, m_NumComponents); - } - - inline static unsigned GetDataPtrOffset(MethodTable* pMT); - - inline static unsigned GetBoundsOffset(MethodTable* pMT); - inline static unsigned GetLowerBoundsOffset(MethodTable* pMT); - - friend struct ::cdac_data; -}; - -#ifndef DACCESS_COMPILE -template<> -struct cdac_data -{ - static constexpr size_t m_NumComponents = offsetof(ArrayBase, m_NumComponents); - - static constexpr INT32* ArrayBoundsZero = &ArrayBase::s_arrayBoundsZero; -}; -#endif - -// -// Template used to build all the non-object -// arrays of a single dimension -// - -template < class KIND > -class Array : public ArrayBase -{ - public: - - typedef DPTR(KIND) PTR_KIND; - - KIND m_Array[1]; - - PTR_KIND GetDirectPointerToNonObjectElements() const - { - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - return dac_cast(PTR_HOST_MEMBER_TADDR(Array, this, m_Array)); - } -}; - - -// Warning: Use PtrArray only for single dimensional arrays, not multidim arrays. -class PtrArray : public ArrayBase -{ - friend class GCHeap; - friend class ClrDataAccess; - friend class JIT_TrialAlloc; - friend class CheckAsmOffsets; - -public: - TypeHandle GetArrayElementTypeHandle() - { - LIMITED_METHOD_CONTRACT; - return GetMethodTable()->GetArrayElementTypeHandle(); - } - - PTR_OBJECTREF GetDataPtr() - { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - return dac_cast(dac_cast(this) + GetDataOffset()); - } - - static SIZE_T GetDataOffset() - { - LIMITED_METHOD_CONTRACT; - return offsetof(PtrArray, m_Array); - } - - void SetAt(SIZE_T i, OBJECTREF ref) - { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - } - CONTRACTL_END; - _ASSERTE(i < GetNumComponents()); - SetObjectReference(m_Array + i, ref); - } - - void ClearAt(SIZE_T i) - { - WRAPPER_NO_CONTRACT; - _ASSERTE(i < GetNumComponents()); - ClearObjectReference(m_Array + i); - } - - OBJECTREF GetAt(SIZE_T i) - { - LIMITED_METHOD_CONTRACT; - SUPPORTS_DAC; - _ASSERTE(i < GetNumComponents()); - -// DAC doesn't know the true size of this array -// the compiler thinks it is size 1, but really it is size N hanging off the structure -#ifndef DACCESS_COMPILE - return m_Array[i]; -#else - TADDR arrayTargetAddress = dac_cast(this) + offsetof(PtrArray, m_Array); - __ArrayDPtr targetArray = dac_cast< __ArrayDPtr >(arrayTargetAddress); - return targetArray[i]; -#endif - } - - friend class StubLinkerCPU; - friend class ArrayOpLinker; -public: - OBJECTREF m_Array[1]; -}; - -#define OFFSETOF__PtrArray__m_Array_ ARRAYBASE_SIZE - -/* a TypedByRef is a structure that is used to implement VB's BYREF variants. - it is basically a tuple of an address of some data along with a TypeHandle - that indicates the type of the address */ -class TypedByRef -{ -public: - - PTR_VOID data; - TypeHandle type; -}; - -typedef DPTR(TypedByRef) PTR_TypedByRef; - -typedef Array I1Array; -typedef Array I2Array; -typedef Array I4Array; -typedef Array I8Array; -typedef Array R4Array; -typedef Array R8Array; -typedef Array U1Array; -typedef Array BOOLArray; -typedef Array U2Array; -typedef Array CHARArray; -typedef Array U4Array; -typedef Array U8Array; -typedef Array UPTRArray; -typedef PtrArray PTRArray; - -typedef DPTR(I1Array) PTR_I1Array; -typedef DPTR(I2Array) PTR_I2Array; -typedef DPTR(I4Array) PTR_I4Array; -typedef DPTR(I8Array) PTR_I8Array; -typedef DPTR(R4Array) PTR_R4Array; -typedef DPTR(R8Array) PTR_R8Array; -typedef DPTR(U1Array) PTR_U1Array; -typedef DPTR(BOOLArray) PTR_BOOLArray; -typedef DPTR(U2Array) PTR_U2Array; -typedef DPTR(CHARArray) PTR_CHARArray; -typedef DPTR(U4Array) PTR_U4Array; -typedef DPTR(U8Array) PTR_U8Array; -typedef DPTR(UPTRArray) PTR_UPTRArray; -typedef DPTR(PTRArray) PTR_PTRArray; - -class StringObject; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF BASEARRAYREF; -typedef REF I1ARRAYREF; -typedef REF I2ARRAYREF; -typedef REF I4ARRAYREF; -typedef REF I8ARRAYREF; -typedef REF R4ARRAYREF; -typedef REF R8ARRAYREF; -typedef REF U1ARRAYREF; -typedef REF BOOLARRAYREF; -typedef REF U2ARRAYREF; -typedef REF U4ARRAYREF; -typedef REF U8ARRAYREF; -typedef REF UPTRARRAYREF; -typedef REF CHARARRAYREF; -typedef REF PTRARRAYREF; // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays. -typedef REF STRINGREF; - -#else // USE_CHECKED_OBJECTREFS - -typedef PTR_ArrayBase BASEARRAYREF; -typedef PTR_I1Array I1ARRAYREF; -typedef PTR_I2Array I2ARRAYREF; -typedef PTR_I4Array I4ARRAYREF; -typedef PTR_I8Array I8ARRAYREF; -typedef PTR_R4Array R4ARRAYREF; -typedef PTR_R8Array R8ARRAYREF; -typedef PTR_U1Array U1ARRAYREF; -typedef PTR_BOOLArray BOOLARRAYREF; -typedef PTR_U2Array U2ARRAYREF; -typedef PTR_U4Array U4ARRAYREF; -typedef PTR_U8Array U8ARRAYREF; -typedef PTR_UPTRArray UPTRARRAYREF; -typedef PTR_CHARArray CHARARRAYREF; -typedef PTR_PTRArray PTRARRAYREF; // Warning: Use PtrArray only for single dimensional arrays, not multidim arrays. -typedef PTR_StringObject STRINGREF; - -#endif // USE_CHECKED_OBJECTREFS - - -#include - - -/* - * StringObject - * - * Special String implementation for performance. - * - * m_StringLength - Length of string in number of WCHARs - * m_FirstChar - The string buffer - * - */ - - -class StringObject : public Object -{ -#ifdef DACCESS_COMPILE - friend class ClrDataAccess; -#endif - friend class GCHeap; - friend class JIT_TrialAlloc; - friend class CheckAsmOffsets; - friend class COMString; - - private: - DWORD m_StringLength; - WCHAR m_FirstChar; - - public: - VOID SetStringLength(DWORD len) { LIMITED_METHOD_CONTRACT; _ASSERTE(len >= 0); m_StringLength = len; } - - protected: - StringObject() {LIMITED_METHOD_CONTRACT; } - ~StringObject() {LIMITED_METHOD_CONTRACT; } - - public: - static DWORD GetBaseSize(); - static SIZE_T GetSize(DWORD stringLength); - - DWORD GetStringLength() { LIMITED_METHOD_DAC_CONTRACT; return( m_StringLength );} - WCHAR* GetBuffer() { LIMITED_METHOD_CONTRACT; return (WCHAR*)( dac_cast(this) + offsetof(StringObject, m_FirstChar) ); } - - static UINT GetBufferOffset() - { - LIMITED_METHOD_DAC_CONTRACT; - return (UINT)(offsetof(StringObject, m_FirstChar)); - } - static UINT GetStringLengthOffset() - { - LIMITED_METHOD_CONTRACT; - return (UINT)(offsetof(StringObject, m_StringLength)); - } - VOID GetSString(SString &result) - { - WRAPPER_NO_CONTRACT; - result.Set(GetBuffer(), GetStringLength()); - } - //======================================================================== - // Creates a System.String object. All the functions that take a length - // or a count of bytes will add the null terminator after length - // characters. So this means that if you have a string that has 5 - // characters and the null terminator you should pass in 5 and NOT 6. - //======================================================================== - static STRINGREF NewString(int length); - static STRINGREF NewString(const WCHAR *pwsz); - static STRINGREF NewString(const WCHAR *pwsz, int length); - static STRINGREF NewString(LPCUTF8 psz); - static STRINGREF NewString(LPCUTF8 psz, int cBytes); - - static STRINGREF GetEmptyString(); - static STRINGREF* GetEmptyStringRefPtr(void** pinnedString); - - static STRINGREF* InitEmptyStringRefPtr(); - - /*=================RefInterpretGetStringValuesDangerousForGC====================== - **N.B.: This performs no range checking and relies on the caller to have done this. - **Args: (IN)ref -- the String to be interpretted. - ** (OUT)chars -- a pointer to the characters in the buffer. - ** (OUT)length -- a pointer to the length of the buffer. - **Returns: void. - **Exceptions: None. - ==============================================================================*/ - // !!!! If you use this function, you have to be careful because chars is a pointer - // !!!! to the data buffer of ref. If GC happens after this call, you need to make - // !!!! sure that you have a pin handle on ref, or use GCPROTECT_BEGINPINNING on ref. - void RefInterpretGetStringValuesDangerousForGC(_Outptr_result_buffer_(*length + 1) WCHAR **chars, int *length) { - WRAPPER_NO_CONTRACT; - - _ASSERTE(GetGCSafeMethodTable() == g_pStringClass); - *length = GetStringLength(); - *chars = GetBuffer(); -#ifdef _DEBUG - EnableStressHeapHelper(); -#endif - } - - -private: - static STRINGREF* EmptyStringRefPtr; - static bool EmptyStringIsFrozen; - - friend struct ::cdac_data; -}; - -template<> -struct cdac_data -{ - static constexpr size_t m_FirstChar = offsetof(StringObject, m_FirstChar); - static constexpr size_t m_StringLength = offsetof(StringObject, m_StringLength); -}; - -/*================================GetEmptyString================================ -**Get a reference to the empty string. If we haven't already gotten one, we -**query the String class for a pointer to the empty string that we know was -**created at startup. -** -**Args: None -**Returns: A STRINGREF to the EmptyString -**Exceptions: None -==============================================================================*/ -inline STRINGREF StringObject::GetEmptyString() { - - CONTRACTL { - THROWS; - MODE_COOPERATIVE; - GC_TRIGGERS; - } CONTRACTL_END; - STRINGREF* refptr = EmptyStringRefPtr; - - //If we've never gotten a reference to the EmptyString, we need to go get one. - if (refptr==NULL) { - refptr = InitEmptyStringRefPtr(); - } - //We've already have a reference to the EmptyString, so we can just return it. - return *refptr; -} - -inline STRINGREF* StringObject::GetEmptyStringRefPtr(void** pinnedString) { - - CONTRACTL { - THROWS; - MODE_ANY; - GC_TRIGGERS; - } CONTRACTL_END; - - STRINGREF* refptr = EmptyStringRefPtr; - - //If we've never gotten a reference to the EmptyString, we need to go get one. - if (refptr == nullptr) - { - refptr = InitEmptyStringRefPtr(); - } - - if (EmptyStringIsFrozen && pinnedString != nullptr) - { - *pinnedString = *(void**)refptr; - } - - //We've already have a reference to the EmptyString, so we can just return it. - return refptr; -} - -// This is the Class version of the Reflection object. -// A Class has adddition information. -// For a ReflectClassBaseObject the m_pData is a pointer to a FieldDesc array that -// contains all of the final static primitives if its defined. -// m_cnt = the number of elements defined in the m_pData FieldDesc array. -1 means -// this hasn't yet been defined. -class ReflectClassBaseObject : public Object -{ - friend class CoreLibBinder; - -protected: - OBJECTREF m_keepalive; - OBJECTREF m_cache; - TypeHandle m_typeHandle; - -#ifdef _DEBUG - void TypeCheck() - { - CONTRACTL - { - NOTHROW; - MODE_COOPERATIVE; - GC_NOTRIGGER; - } - CONTRACTL_END; - - MethodTable *pMT = GetMethodTable(); - while (pMT != g_pRuntimeTypeClass && pMT != NULL) - { - pMT = pMT->GetParentMethodTable(); - } - _ASSERTE(pMT == g_pRuntimeTypeClass); - } -#endif // _DEBUG - -public: - void SetType(TypeHandle type) { - CONTRACTL - { - NOTHROW; - MODE_COOPERATIVE; - GC_NOTRIGGER; - } - CONTRACTL_END; - - INDEBUG(TypeCheck()); - m_typeHandle = type; - } - - void SetKeepAlive(OBJECTREF keepalive) - { - CONTRACTL - { - NOTHROW; - MODE_COOPERATIVE; - GC_NOTRIGGER; - } - CONTRACTL_END; - - INDEBUG(TypeCheck()); - SetObjectReference(&m_keepalive, keepalive); - } - - TypeHandle GetType() { - CONTRACTL - { - NOTHROW; - MODE_COOPERATIVE; - GC_NOTRIGGER; - } - CONTRACTL_END; - - INDEBUG(TypeCheck()); - return m_typeHandle; - } - -}; - -// This is the Method version of the Reflection object. -// A Method has additional information: -// m_pMD - A pointer to the actual MethodDesc of the method. -// m_object - a field that has a reference type in it. Used only for RuntimeMethodInfoStub to keep the real type alive. -// This structure matches the structure up to the m_pMD for several different managed types. -// (RuntimeConstructorInfo, RuntimeMethodInfo, and RuntimeMethodInfoStub). These types are unrelated in the type -// system except that they all implement a particular interface. It is important that such interface is not attached to any -// type that does not sufficiently match this data structure. -class ReflectMethodObject : public Object -{ - friend class CoreLibBinder; - -protected: - OBJECTREF m_object; - OBJECTREF m_empty1; - OBJECTREF m_empty2; - OBJECTREF m_empty3; - OBJECTREF m_empty4; - OBJECTREF m_empty5; - OBJECTREF m_empty6; - OBJECTREF m_empty7; - OBJECTREF m_empty8; - MethodDesc * m_pMD; - -public: - void SetMethod(MethodDesc *pMethod) { - LIMITED_METHOD_CONTRACT; - m_pMD = pMethod; - } - - // This must only be called on instances of ReflectMethodObject that are actually RuntimeMethodInfoStub - void SetKeepAlive(OBJECTREF keepalive) - { - WRAPPER_NO_CONTRACT; - SetObjectReference(&m_object, keepalive); - } - - MethodDesc *GetMethod() { - LIMITED_METHOD_CONTRACT; - return m_pMD; - } - -}; - -// This is the Field version of the Reflection object. -// A Method has additional information: -// m_pFD - A pointer to the actual MethodDesc of the method. -// m_object - a field that has a reference type in it. Used only for RuntimeFieldInfoStub to keep the real type alive. -// This structure matches the structure up to the m_pFD for several different managed types. -// (RtFieldInfo and RuntimeFieldInfoStub). These types are unrelated in the type -// system except that they all implement a particular interface. It is important that such interface is not attached to any -// type that does not sufficiently match this data structure. -class ReflectFieldObject : public Object -{ - friend class CoreLibBinder; - -protected: - OBJECTREF m_object; - OBJECTREF m_empty1; - INT32 m_empty2; - OBJECTREF m_empty3; - OBJECTREF m_empty4; - OBJECTREF m_empty5; - FieldDesc * m_pFD; - -public: - void SetField(FieldDesc *pField) { - LIMITED_METHOD_CONTRACT; - m_pFD = pField; - } - - // This must only be called on instances of ReflectFieldObject that are actually RuntimeFieldInfoStub - void SetKeepAlive(OBJECTREF keepalive) - { - WRAPPER_NO_CONTRACT; - SetObjectReference(&m_object, keepalive); - } - - FieldDesc *GetField() { - LIMITED_METHOD_CONTRACT; - return m_pFD; - } -}; - -// ReflectModuleBaseObject -// This class is the base class for managed Module. -// This class will connect the Object back to the underlying VM representation -// m_ReflectClass -- This is the real Class that was used for reflection -// This class was used to get at this object -// m_pData -- this is a generic pointer which usually points CorModule -// -class ReflectModuleBaseObject : public Object -{ - friend class CoreLibBinder; - - protected: - // READ ME: - // Modifying the order or fields of this object may require other changes to the - // classlib class definition of this object. - OBJECTREF m_runtimeType; - OBJECTREF m_runtimeAssembly; - Module* m_pData; // Pointer to the Module - - protected: - ReflectModuleBaseObject() {LIMITED_METHOD_CONTRACT;} - ~ReflectModuleBaseObject() {LIMITED_METHOD_CONTRACT;} - - public: - void SetModule(Module* p) { - LIMITED_METHOD_CONTRACT; - m_pData = p; - } - Module* GetModule() { - LIMITED_METHOD_CONTRACT; - return m_pData; - } - void SetAssembly(OBJECTREF assembly) - { - WRAPPER_NO_CONTRACT; - SetObjectReference(&m_runtimeAssembly, assembly); - } -}; - -class ThreadBaseObject; -class ExecutionContextObject: public Object -{ - friend class CoreLibBinder; - -private: - // These field are also defined in the managed representation. (SecurityContext.cs)If you - // add or change these field you must also change the managed code so that - // it matches these. This is necessary so that the object is the proper - // size. - OBJECTREF m_localValues; - OBJECTREF m_localChangeNotifications; - CLR_BOOL m_isFlowSuppressed; - -public: - bool IsFlowSuppressed() const - { - LIMITED_METHOD_CONTRACT; - return m_isFlowSuppressed; - } -}; - -typedef DPTR(class CultureInfoBaseObject) PTR_CultureInfoBaseObject; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF EXECUTIONCONTEXTREF; -typedef REF ARRAYBASEREF; - -#else -typedef ExecutionContextObject* EXECUTIONCONTEXTREF; -typedef PTR_ArrayBase ARRAYBASEREF; -#endif - -typedef DPTR(class ThreadBaseObject) PTR_ThreadBaseObject; -class ThreadBaseObject : public Object -{ - friend class ClrDataAccess; - friend class CoreLibBinder; - friend class Object; - -private: - - // These field are also defined in the managed representation. If you - // add or change these field you must also change the managed code so that - // it matches these. This is necessary so that the object is the proper - // size. The order here must match that order which the loader will choose - // when laying out the managed class. Note that the layouts are checked - // at run time, not compile time. - OBJECTREF m_ExecutionContext; - OBJECTREF m_SynchronizationContext; - STRINGREF m_Name; - OBJECTREF m_StartHelper; -#ifdef TARGET_UNIX - OBJECTREF m_WaitInfo; - OBJECTREF m_joinEvent; -#endif // TARGET_UNIX - - // The next field (m_InternalThread) is declared as IntPtr in the managed - // definition of Thread. The loader will sort it next. - - // m_InternalThread is always valid -- unless the thread has finalized and been - // resurrected. (The thread stopped running before it was finalized). - Thread *m_InternalThread; - INT32 m_Priority; - - // We need to cache the thread id in managed code for perf reasons. - INT32 m_ManagedThreadId; - - // Only used by managed code, see comment there - bool m_MayNeedResetForThreadPool; - - // Set in unmanaged code and read in managed code. - bool m_IsDead; - - bool m_IsThreadPool; - -protected: - // the ctor and dtor can do no useful work. - ThreadBaseObject() {LIMITED_METHOD_CONTRACT;}; - ~ThreadBaseObject() {LIMITED_METHOD_CONTRACT;}; - -public: - Thread *GetInternal() - { - LIMITED_METHOD_CONTRACT; - return m_InternalThread; - } - - void SetInternal(Thread *it); - void ClearInternal(); - - void SetManagedThreadId(INT32 id) - { - LIMITED_METHOD_CONTRACT; - m_ManagedThreadId = id; - } - - STRINGREF GetName() { - LIMITED_METHOD_CONTRACT; - return m_Name; - } - - OBJECTREF GetExecutionContext() - { - LIMITED_METHOD_CONTRACT; - return m_ExecutionContext; - } - - void InitExisting(); - - void ResetStartHelper() - { - LIMITED_METHOD_CONTRACT - m_StartHelper = NULL; - } - - void SetPriority(INT32 priority) - { - LIMITED_METHOD_CONTRACT; - m_Priority = priority; - } - - INT32 GetPriority() const - { - LIMITED_METHOD_CONTRACT; - return m_Priority; - } - - void SetIsDead() - { - LIMITED_METHOD_CONTRACT; - m_IsDead = true; - } -}; - -// AssemblyBaseObject -// This class is the base class for assemblies -// -class AssemblyBaseObject : public Object -{ - friend class Assembly; - friend class CoreLibBinder; - - protected: - // READ ME: - // Modifying the order or fields of this object may require other changes to the - // classlib class definition of this object. - OBJECTREF m_pModuleEventHandler; // Delegate for 'resolve module' event - STRINGREF m_fullname; // Slot for storing assemblies fullname - OBJECTREF m_pSyncRoot; // Pointer to loader allocator to keep collectible types alive, and to serve as the syncroot for assembly building in ref.emit - Assembly* m_pAssembly; // Pointer to the Assembly Structure - - protected: - AssemblyBaseObject() { LIMITED_METHOD_CONTRACT; } - ~AssemblyBaseObject() { LIMITED_METHOD_CONTRACT; } - - public: - - void SetAssembly(Assembly* p) - { - LIMITED_METHOD_CONTRACT; - m_pAssembly = p; - } - - Assembly* GetAssembly(); - - void SetSyncRoot(OBJECTREF pSyncRoot) - { - WRAPPER_NO_CONTRACT; - SetObjectReference(&m_pSyncRoot, pSyncRoot); - } -}; - -// AssemblyLoadContextBaseObject -// This class is the base class for AssemblyLoadContext -// -#if defined(TARGET_X86) && !defined(TARGET_UNIX) -#include "pshpack4.h" -#endif // defined(TARGET_X86) && !defined(TARGET_UNIX) -class AssemblyLoadContextBaseObject : public Object -{ - friend class CoreLibBinder; - - protected: - // READ ME: - // Modifying the order or fields of this object may require other changes to the - // classlib class definition of this object. -#ifdef TARGET_64BIT - OBJECTREF _unloadLock; - OBJECTREF _resolvingUnmanagedDll; - OBJECTREF _resolving; - OBJECTREF _unloading; - OBJECTREF _name; - INT_PTR _nativeAssemblyLoadContext; - int64_t _id; // On 64-bit platforms this is a value type so it is placed after references and pointers - DWORD _state; - CLR_BOOL _isCollectible; -#else // TARGET_64BIT - int64_t _id; // On 32-bit platforms this 64-bit value type is larger than a pointer so JIT places it first - OBJECTREF _unloadLock; - OBJECTREF _resolvingUnmanagedDll; - OBJECTREF _resolving; - OBJECTREF _unloading; - OBJECTREF _name; - INT_PTR _nativeAssemblyLoadContext; - DWORD _state; - CLR_BOOL _isCollectible; -#endif // TARGET_64BIT - - protected: - AssemblyLoadContextBaseObject() { LIMITED_METHOD_CONTRACT; } - ~AssemblyLoadContextBaseObject() { LIMITED_METHOD_CONTRACT; } - - public: - INT_PTR GetNativeAssemblyBinder() { LIMITED_METHOD_CONTRACT; return _nativeAssemblyLoadContext; } -}; -#if defined(TARGET_X86) && !defined(TARGET_UNIX) -#include "poppack.h" -#endif // defined(TARGET_X86) && !defined(TARGET_UNIX) - -struct NativeAssemblyNameParts -{ - PCWSTR _pName; - UINT16 _major, _minor, _build, _revision; - PCWSTR _pCultureName; - BYTE* _pPublicKeyOrToken; - int _cbPublicKeyOrToken; - DWORD _flags; -}; - -// AssemblyNameBaseObject -// This class is the base class for assembly names -// -class AssemblyNameBaseObject : public Object -{ - // Dummy definition -}; - -class WeakReferenceObject : public Object -{ -public: - uintptr_t m_taggedHandle; -}; - - -typedef DPTR(class ContinuationObject) PTR_ContinuationObject; -class ContinuationObject; - -#ifdef USE_CHECKED_OBJECTREFS - -typedef REF REFLECTMODULEBASEREF; - -typedef REF REFLECTCLASSBASEREF; - -typedef REF REFLECTMETHODREF; - -typedef REF REFLECTFIELDREF; - -typedef REF THREADBASEREF; - -typedef REF ASSEMBLYREF; - -typedef REF ASSEMBLYLOADCONTEXTREF; - -typedef REF ASSEMBLYNAMEREF; - -typedef REF THREADBASEREF; - -typedef REF CONTINUATIONREF; - -inline ARG_SLOT ObjToArgSlot(OBJECTREF objRef) -{ - LIMITED_METHOD_CONTRACT; - LPVOID v; - v = OBJECTREFToObject(objRef); - return (ARG_SLOT)(SIZE_T)v; -} - -inline OBJECTREF ArgSlotToObj(ARG_SLOT i) -{ - LIMITED_METHOD_CONTRACT; - LPVOID v; - v = (LPVOID)(SIZE_T)i; - return ObjectToOBJECTREF ((Object*)v); -} - -inline ARG_SLOT StringToArgSlot(STRINGREF sr) -{ - LIMITED_METHOD_CONTRACT; - LPVOID v; - v = OBJECTREFToObject(sr); - return (ARG_SLOT)(SIZE_T)v; -} - -inline STRINGREF ArgSlotToString(ARG_SLOT s) -{ - LIMITED_METHOD_CONTRACT; - LPVOID v; - v = (LPVOID)(SIZE_T)s; - return ObjectToSTRINGREF ((StringObject*)v); -} - -#else // USE_CHECKED_OBJECTREFS - -typedef PTR_ReflectModuleBaseObject REFLECTMODULEBASEREF; -typedef PTR_ReflectClassBaseObject REFLECTCLASSBASEREF; -typedef PTR_ReflectMethodObject REFLECTMETHODREF; -typedef PTR_ReflectFieldObject REFLECTFIELDREF; -typedef PTR_ThreadBaseObject THREADBASEREF; -typedef PTR_AssemblyBaseObject ASSEMBLYREF; -typedef PTR_AssemblyLoadContextBaseObject ASSEMBLYLOADCONTEXTREF; -typedef PTR_AssemblyNameBaseObject ASSEMBLYNAMEREF; -typedef PTR_ThreadBaseObject THREADBASEREF; -typedef PTR_ContinuationObject CONTINUATIONREF; - -#define ObjToArgSlot(objref) ((ARG_SLOT)(SIZE_T)(objref)) -#define ArgSlotToObj(s) ((OBJECTREF)(SIZE_T)(s)) - -#define StringToArgSlot(objref) ((ARG_SLOT)(SIZE_T)(objref)) -#define ArgSlotToString(s) ((STRINGREF)(SIZE_T)(s)) - -#endif //USE_CHECKED_OBJECTREFS - -#define PtrToArgSlot(ptr) ((ARG_SLOT)(SIZE_T)(ptr)) -#define ArgSlotToPtr(s) ((LPVOID)(SIZE_T)(s)) - -#define BoolToArgSlot(b) ((ARG_SLOT)(CLR_BOOL)(!!(b))) -#define ArgSlotToBool(s) ((BOOL)(s)) - -STRINGREF AllocateString(SString sstr); - -#ifdef FEATURE_COMINTEROP - -//------------------------------------------------------------- -// class ComObject, Exposed class __ComObject -// -// -//------------------------------------------------------------- -class ComObject : public Object -{ - friend class CoreLibBinder; - -protected: - - ComObject() - {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly - ~ComObject(){LIMITED_METHOD_CONTRACT;}; - -public: - OBJECTREF m_ObjectToDataMap; - - //-------------------------------------------------------------------- - // SupportsInterface - static BOOL SupportsInterface(OBJECTREF oref, MethodTable* pIntfTable); - - //-------------------------------------------------------------------- - // SupportsInterface - static void ThrowInvalidCastException(OBJECTREF *pObj, MethodTable* pCastToMT); - - //----------------------------------------------------------------- - // GetComIPFromRCW - static IUnknown* GetComIPFromRCW(OBJECTREF *pObj, MethodTable* pIntfTable); - - //----------------------------------------------------------------- - // GetComIPFromRCWThrowing - static IUnknown* GetComIPFromRCWThrowing(OBJECTREF *pObj, MethodTable* pIntfTable); - - //----------------------------------------------------------- - // create an empty ComObjectRef - static OBJECTREF CreateComObjectRef(MethodTable* pMT); - - //----------------------------------------------------------- - // Release all the data associated with the __ComObject. - static void ReleaseAllData(OBJECTREF oref); -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF COMOBJECTREF; -#else -typedef ComObject* COMOBJECTREF; -#endif - - -//------------------------------------------------------------- -// class UnknownWrapper, Exposed class UnknownWrapper -// -// -//------------------------------------------------------------- -class UnknownWrapper : public Object -{ -protected: - - UnknownWrapper(UnknownWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. - UnknownWrapper() {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly - ~UnknownWrapper() {LIMITED_METHOD_CONTRACT;}; - - OBJECTREF m_WrappedObject; - -public: - OBJECTREF GetWrappedObject() - { - LIMITED_METHOD_CONTRACT; - return m_WrappedObject; - } - - void SetWrappedObject(OBJECTREF pWrappedObject) - { - LIMITED_METHOD_CONTRACT; - m_WrappedObject = pWrappedObject; - } -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF UNKNOWNWRAPPEROBJECTREF; -#else -typedef UnknownWrapper* UNKNOWNWRAPPEROBJECTREF; -#endif - - -//------------------------------------------------------------- -// class DispatchWrapper, Exposed class DispatchWrapper -// -// -//------------------------------------------------------------- -class DispatchWrapper : public Object -{ -protected: - - DispatchWrapper(DispatchWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. - DispatchWrapper() {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly - ~DispatchWrapper() {LIMITED_METHOD_CONTRACT;}; - - OBJECTREF m_WrappedObject; - -public: - OBJECTREF GetWrappedObject() - { - LIMITED_METHOD_CONTRACT; - return m_WrappedObject; - } - - void SetWrappedObject(OBJECTREF pWrappedObject) - { - LIMITED_METHOD_CONTRACT; - m_WrappedObject = pWrappedObject; - } -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF DISPATCHWRAPPEROBJECTREF; -#else -typedef DispatchWrapper* DISPATCHWRAPPEROBJECTREF; -#endif - - -//------------------------------------------------------------- -// class VariantWrapper, Exposed class VARIANTWRAPPEROBJECTREF -// -// -//------------------------------------------------------------- -class VariantWrapper : public Object -{ -protected: - - VariantWrapper(VariantWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. - VariantWrapper() {LIMITED_METHOD_CONTRACT}; // don't instantiate this class directly - ~VariantWrapper() {LIMITED_METHOD_CONTRACT}; - - OBJECTREF m_WrappedObject; - -public: - OBJECTREF GetWrappedObject() - { - LIMITED_METHOD_CONTRACT; - return m_WrappedObject; - } - - void SetWrappedObject(OBJECTREF pWrappedObject) - { - LIMITED_METHOD_CONTRACT; - m_WrappedObject = pWrappedObject; - } -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF VARIANTWRAPPEROBJECTREF; -#else -typedef VariantWrapper* VARIANTWRAPPEROBJECTREF; -#endif - - -//------------------------------------------------------------- -// class ErrorWrapper, Exposed class ErrorWrapper -// -// -//------------------------------------------------------------- -class ErrorWrapper : public Object -{ -protected: - - ErrorWrapper(ErrorWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. - ErrorWrapper() {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly - ~ErrorWrapper() {LIMITED_METHOD_CONTRACT;}; - - INT32 m_ErrorCode; - -public: - INT32 GetErrorCode() - { - LIMITED_METHOD_CONTRACT; - return m_ErrorCode; - } - - void SetErrorCode(int ErrorCode) - { - LIMITED_METHOD_CONTRACT; - m_ErrorCode = ErrorCode; - } -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF ERRORWRAPPEROBJECTREF; -#else -typedef ErrorWrapper* ERRORWRAPPEROBJECTREF; -#endif - - -//------------------------------------------------------------- -// class CurrencyWrapper, Exposed class CurrencyWrapper -// -// -//------------------------------------------------------------- - -// Keep this in sync with code:MethodTableBuilder.CheckForSystemTypes where -// alignment requirement of the managed System.Decimal structure is computed. -#if !defined(ALIGN_ACCESS) && !defined(FEATURE_64BIT_ALIGNMENT) -#include -#endif // !ALIGN_ACCESS && !FEATURE_64BIT_ALIGNMENT - -class CurrencyWrapper : public Object -{ -protected: - - CurrencyWrapper(CurrencyWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. - CurrencyWrapper() {LIMITED_METHOD_CONTRACT;}; // don't instantiate this class directly - ~CurrencyWrapper() {LIMITED_METHOD_CONTRACT;}; - - DECIMAL m_WrappedObject; - -public: - DECIMAL GetWrappedObject() - { - LIMITED_METHOD_CONTRACT; - return m_WrappedObject; - } - - void SetWrappedObject(DECIMAL WrappedObj) - { - LIMITED_METHOD_CONTRACT; - m_WrappedObject = WrappedObj; - } -}; - -#if !defined(ALIGN_ACCESS) && !defined(FEATURE_64BIT_ALIGNMENT) -#include -#endif // !ALIGN_ACCESS && !FEATURE_64BIT_ALIGNMENT - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF CURRENCYWRAPPEROBJECTREF; -#else -typedef CurrencyWrapper* CURRENCYWRAPPEROBJECTREF; -#endif - -//------------------------------------------------------------- -// class BStrWrapper, Exposed class BSTRWRAPPEROBJECTREF -// -// -//------------------------------------------------------------- -class BStrWrapper : public Object -{ -protected: - - BStrWrapper(BStrWrapper &wrap) {LIMITED_METHOD_CONTRACT}; // disallow copy construction. - BStrWrapper() {LIMITED_METHOD_CONTRACT}; // don't instantiate this class directly - ~BStrWrapper() {LIMITED_METHOD_CONTRACT}; - - STRINGREF m_WrappedObject; - -public: - STRINGREF GetWrappedObject() - { - LIMITED_METHOD_CONTRACT; - return m_WrappedObject; - } - - void SetWrappedObject(STRINGREF pWrappedObject) - { - LIMITED_METHOD_CONTRACT; - m_WrappedObject = pWrappedObject; - } -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF BSTRWRAPPEROBJECTREF; -#else -typedef BStrWrapper* BSTRWRAPPEROBJECTREF; -#endif - -#endif // FEATURE_COMINTEROP - - -// This class corresponds to System.MulticastDelegate on the managed side. -class DelegateObject : public Object -{ - friend class CheckAsmOffsets; - friend class CoreLibBinder; - friend struct ::cdac_data; - -public: - BOOL IsWrapperDelegate() { LIMITED_METHOD_CONTRACT; return _methodPtrAux == 0; } - - OBJECTREF GetTarget() { LIMITED_METHOD_CONTRACT; return _target; } - void SetTarget(OBJECTREF target) { WRAPPER_NO_CONTRACT; SetObjectReference(&_target, target); } - static int GetOffsetOfTarget() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _target); } - - MethodDesc* GetMethodDesc() { LIMITED_METHOD_CONTRACT; return _methodDesc; } - void SetMethodDesc(MethodDesc* methodDesc) { LIMITED_METHOD_CONTRACT; _methodDesc = methodDesc; } - static int GetOffsetOfMethodDesc() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodDesc); } - - PCODE GetMethodPtr() { LIMITED_METHOD_CONTRACT; return _methodPtr; } - void SetMethodPtr(PCODE methodPtr) { LIMITED_METHOD_CONTRACT; _methodPtr = methodPtr; } - static int GetOffsetOfMethodPtr() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtr); } - - PCODE GetMethodPtrAux() { LIMITED_METHOD_CONTRACT; return _methodPtrAux; } - void SetMethodPtrAux(PCODE methodPtrAux) { LIMITED_METHOD_CONTRACT; _methodPtrAux = methodPtrAux; } - static int GetOffsetOfMethodPtrAux() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtrAux); } - - OBJECTREF GetInvocationList() { LIMITED_METHOD_CONTRACT; return _invocationList; } - void SetInvocationList(OBJECTREF invocationList) { WRAPPER_NO_CONTRACT; SetObjectReference(&_invocationList, invocationList); } - static int GetOffsetOfInvocationList() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _invocationList); } - - INT_PTR GetInvocationCount() { LIMITED_METHOD_CONTRACT; return _invocationCount; } - void SetInvocationCount(INT_PTR invocationCount) { LIMITED_METHOD_CONTRACT; _invocationCount = invocationCount; } - static int GetOffsetOfInvocationCount() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _invocationCount); } - - // README: - // If you modify the order of these fields, make sure to update the definition in - // BCL for this object. -private: - // System.Delegate -<<<<<<< HEAD - OBJECTREF _target; - MethodDesc* _methodDesc; -======= - OBJECTREF _methodBase; - OBJECTREF _target; ->>>>>>> upstream/main - PCODE _methodPtr; - PCODE _methodPtrAux; - // System.MulticastDelegate - OBJECTREF _invocationList; - INT_PTR _invocationCount; -}; - -<<<<<<< HEAD -#define OFFSETOF__DelegateObject__target OBJECT_SIZE /* m_pMethTab */ -#define OFFSETOF__DelegateObject__methodDesc (OFFSETOF__DelegateObject__target + TARGET_POINTER_SIZE /* _target */) -#define OFFSETOF__DelegateObject__methodPtr (OFFSETOF__DelegateObject__methodDesc + TARGET_POINTER_SIZE /* _methodDesc */) -======= -#define OFFSETOF__DelegateObject__target (OBJECT_SIZE /* m_pMethTab */ + TARGET_POINTER_SIZE /* _methodBase */) -#define OFFSETOF__DelegateObject__methodPtr (OFFSETOF__DelegateObject__target + TARGET_POINTER_SIZE /* _target */) ->>>>>>> upstream/main -#define OFFSETOF__DelegateObject__methodPtrAux (OFFSETOF__DelegateObject__methodPtr + TARGET_POINTER_SIZE /* _methodPtr */) - -template<> -struct cdac_data -{ - static constexpr size_t Target = offsetof(DelegateObject, _target); - static constexpr size_t MethodPtr = offsetof(DelegateObject, _methodPtr); - static constexpr size_t MethodPtrAux = offsetof(DelegateObject, _methodPtrAux); - static constexpr size_t InvocationCount = offsetof(DelegateObject, _invocationCount); -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF DELEGATEREF; -#else // USE_CHECKED_OBJECTREFS -typedef DelegateObject* DELEGATEREF; -#endif // USE_CHECKED_OBJECTREFS - - -struct StackTraceElement; -class ClrDataAccess; - - -typedef DPTR(StackTraceElement) PTR_StackTraceElement; - -class StackTraceArray -{ - struct ArrayHeader - { - uint32_t m_size; - uint32_t m_keepAliveItemsCount; - Thread * m_thread; - }; - - typedef DPTR(ArrayHeader) PTR_ArrayHeader; - -public: - StackTraceArray() - : m_array(static_cast(NULL)) - { - WRAPPER_NO_CONTRACT; - } - - StackTraceArray(I1ARRAYREF array) - : m_array(array) - { - LIMITED_METHOD_CONTRACT; - } - - void Set(I1ARRAYREF array) - { - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - } - CONTRACTL_END; - SUPPORTS_DAC; - m_array = array; - } - - uint32_t Size() const - { - WRAPPER_NO_CONTRACT; - if (!m_array) - return 0; - else - return GetSize(); - } - - StackTraceElement const & operator[](size_t index) const; - StackTraceElement & operator[](size_t index); - - size_t Capacity() const; - void Append(StackTraceElement const * element); - void Allocate(size_t size); - - I1ARRAYREF Get() const - { - LIMITED_METHOD_DAC_CONTRACT; - return m_array; - } - - uint32_t CopyDataFrom(StackTraceArray const & src); - - Thread * GetObjectThread() const - { - WRAPPER_NO_CONTRACT; - return GetHeader()->m_thread; - } - - void SetSize(uint32_t size) - { - WRAPPER_NO_CONTRACT; - VolatileStore(&GetHeader()->m_size, size); - } - - void SetKeepAliveItemsCount(uint32_t count) - { - WRAPPER_NO_CONTRACT; - VolatileStore(&GetHeader()->m_keepAliveItemsCount, count); - } - - uint32_t GetKeepAliveItemsCount() const - { - WRAPPER_NO_CONTRACT; - return VolatileLoad(&GetHeader()->m_keepAliveItemsCount); - } - - // Compute the number of methods in the stack trace that can be collected. We need to store keepAlive - // objects (Resolver / LoaderAllocator) for these methods. - uint32_t ComputeKeepAliveItemsCount(); - - void MarkAsFrozen() - { - if (m_array != NULL) - { - GetHeader()->m_thread = (Thread *)(size_t)1; - } - } - - bool IsFrozen() const - { - return GetHeader()->m_thread == (Thread *)(size_t)1; - } - -private: - StackTraceArray(StackTraceArray const & rhs) = delete; - - StackTraceArray & operator=(StackTraceArray const & rhs) = delete; - - void CheckState() const; - - uint32_t GetSize() const - { - WRAPPER_NO_CONTRACT; - return VolatileLoad(&GetHeader()->m_size); - } - - void SetObjectThread() - { - WRAPPER_NO_CONTRACT; - GetHeader()->m_thread = GetThread(); - } - - StackTraceElement const * GetData() const - { - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - return dac_cast(GetRaw() + sizeof(ArrayHeader)); - } - - PTR_StackTraceElement GetData() - { - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - return dac_cast(GetRaw() + sizeof(ArrayHeader)); - } - - CLR_I1 const * GetRaw() const - { - WRAPPER_NO_CONTRACT; - _ASSERTE(!!m_array); - - return m_array->GetDirectPointerToNonObjectElements(); - } - - PTR_INT8 GetRaw() - { - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - _ASSERTE(!!m_array); - - return dac_cast(m_array->GetDirectPointerToNonObjectElements()); - } - - ArrayHeader const * GetHeader() const - { - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - return dac_cast(GetRaw()); - } - - PTR_ArrayHeader GetHeader() - { - WRAPPER_NO_CONTRACT; - SUPPORTS_DAC; - return dac_cast(GetRaw()); - } - - void SetArray(I1ARRAYREF const & arr) - { - LIMITED_METHOD_CONTRACT; - m_array = arr; - } - -private: - // put only things here that can be protected with GCPROTECT - I1ARRAYREF m_array; - - friend struct ::cdac_data; -}; - -template<> -struct cdac_data -{ - static constexpr size_t Size = offsetof(StackTraceArray::ArrayHeader, m_size); - static constexpr size_t HeaderSize = sizeof(StackTraceArray::ArrayHeader); -}; - -#ifdef FEATURE_COLLECTIBLE_TYPES - -class LoaderAllocatorScoutObject : public Object -{ - friend class CoreLibBinder; - friend class LoaderAllocatorObject; - -protected: - LoaderAllocator * m_nativeLoaderAllocator; -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF LOADERALLOCATORSCOUTREF; -#else // USE_CHECKED_OBJECTREFS -typedef LoaderAllocatorScoutObject* LOADERALLOCATORSCOUTREF; -#endif // USE_CHECKED_OBJECTREFS - -class LoaderAllocatorObject : public Object -{ - friend class CoreLibBinder; - -public: - // All uses of this api must be safe lock-free reads used only for reading from the handle table - // The normal GetHandleTable can only be called while holding the handle table lock, but - // this is for use in lock-free scenarios - PTRARRAYREF DangerousGetHandleTable() - { - LIMITED_METHOD_DAC_CONTRACT; - return (PTRARRAYREF)ObjectToOBJECTREF(VolatileLoadWithoutBarrier((Object**)&m_pSlots)); - } - -#ifndef DACCESS_COMPILE - PTRARRAYREF GetHandleTable(); - void SetHandleTable(PTRARRAYREF handleTable); - INT32 GetSlotsUsed(); - void SetSlotsUsed(INT32 newSlotsUsed); -#endif // DACCESS_COMPILE - - void SetNativeLoaderAllocator(LoaderAllocator * pLoaderAllocator) - { - LIMITED_METHOD_CONTRACT; - m_pLoaderAllocatorScout->m_nativeLoaderAllocator = pLoaderAllocator; - } - - // README: - // If you modify the order of these fields, make sure to update the definition in - // BCL for this object. -protected: - LOADERALLOCATORSCOUTREF m_pLoaderAllocatorScout; - OBJECTREF m_pSlots; - INT32 m_slotsUsed; - OBJECTREF m_methodInstantiationsTable; -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF LOADERALLOCATORREF; -#else // USE_CHECKED_OBJECTREFS -typedef DPTR(LoaderAllocatorObject) PTR_LoaderAllocatorObject; -typedef PTR_LoaderAllocatorObject LOADERALLOCATORREF; -#endif // USE_CHECKED_OBJECTREFS - -#endif // FEATURE_COLLECTIBLE_TYPES - -typedef DPTR(class GenericCacheStruct) PTR_GenericCacheStruct; -class GenericCacheStruct -{ - friend class CoreLibBinder; - public: - - ARRAYBASEREF GetTable() const - { - LIMITED_METHOD_CONTRACT; - return _table; - } - - int32_t CacheElementCount() const - { - LIMITED_METHOD_CONTRACT; - return GetTable()->GetNumComponents() - 1; - } - - ARRAYBASEREF GetSentinelTable() const - { - LIMITED_METHOD_CONTRACT; - return _sentinelTable; - } - - void SetTable(ARRAYBASEREF table) - { - WRAPPER_NO_CONTRACT; - SetObjectReference((OBJECTREF*)&_table, (OBJECTREF)table); - } - - void SetLastFlushSize(int32_t lastFlushSize) - { - LIMITED_METHOD_CONTRACT; - _lastFlushSize = lastFlushSize; - } - - int32_t GetInitialCacheSize() const - { - LIMITED_METHOD_CONTRACT; - return _initialCacheSize; - } - -#ifdef DEBUG - static void ValidateLayout(MethodTable* pMTOfInstantiation); -#endif - - private: - // README: - // If you modify the order of these fields, make sure to update the definition in - // BCL for this object. - - ARRAYBASEREF _table; - ARRAYBASEREF _sentinelTable; - int32_t _lastFlushSize; - int32_t _initialCacheSize; - int32_t _maxCacheSize; -}; - -class ContinuationObject : public Object -{ - friend class CoreLibBinder; - - public: - CorInfoContinuationFlags GetFlags() const - { - LIMITED_METHOD_CONTRACT; - return (CorInfoContinuationFlags)Flags; - } - - void SetFlags(CorInfoContinuationFlags flags) - { - LIMITED_METHOD_CONTRACT; - Flags = (int32_t)flags; - } - - void SetResumeInfo(void* resumeInfo) - { - LIMITED_METHOD_CONTRACT; - ResumeInfo = resumeInfo; - } - - void* GetResumeInfo() const - { - LIMITED_METHOD_CONTRACT; - return ResumeInfo; - } - - void SetState(int32_t state) - { - LIMITED_METHOD_CONTRACT; - State = state; - } - - int32_t GetState() const - { - LIMITED_METHOD_CONTRACT; - return State; - } - - PTR_BYTE GetResultStorage() - { - LIMITED_METHOD_CONTRACT; - - uint32_t mask = (1u << CORINFO_CONTINUATION_RESULT_INDEX_NUM_BITS) - 1; - uint32_t index = ((uint32_t)Flags >> CORINFO_CONTINUATION_RESULT_INDEX_FIRST_BIT) & mask; - _ASSERTE(index != 0); - - uint32_t offset = OFFSETOF__CORINFO_Continuation__data + (index - 1) * TARGET_POINTER_SIZE; - PTR_BYTE dataAddress = dac_cast((dac_cast(this) + offset)); - return dataAddress; - } - - PTR_OBJECTREF GetExceptionObjectStorageOrNull() - { - LIMITED_METHOD_CONTRACT; - - uint32_t mask = (1u << CORINFO_CONTINUATION_EXCEPTION_INDEX_NUM_BITS) - 1; - uint32_t index = ((uint32_t)Flags >> CORINFO_CONTINUATION_EXCEPTION_INDEX_FIRST_BIT) & mask; - if (index == 0) - return NULL; - - uint32_t offset = OFFSETOF__CORINFO_Continuation__data + (index - 1) * TARGET_POINTER_SIZE; - PTR_BYTE dataAddress = dac_cast(dac_cast(this) + offset); - return dac_cast(dataAddress); - } - - PTR_OBJECTREF GetExecutionContextObjectStorageOrNull() - { - LIMITED_METHOD_CONTRACT; - - uint32_t mask = (1u << CORINFO_CONTINUATION_EXECUTION_CONTEXT_INDEX_NUM_BITS) - 1; - uint32_t index = ((uint32_t)Flags >> CORINFO_CONTINUATION_EXECUTION_CONTEXT_INDEX_FIRST_BIT) & mask; - if (index == 0) - return NULL; - - uint32_t offset = OFFSETOF__CORINFO_Continuation__data + (index - 1) * TARGET_POINTER_SIZE; - PTR_BYTE address = dac_cast(dac_cast(this) + offset); - return dac_cast(address); - } - - PTR_OBJECTREF GetContinuationContextObjectStorageOrNull() - { - LIMITED_METHOD_CONTRACT; - - uint32_t mask = (1u << CORINFO_CONTINUATION_CONTEXT_INDEX_NUM_BITS) - 1; - uint32_t index = ((uint32_t)Flags >> CORINFO_CONTINUATION_CONTEXT_INDEX_FIRST_BIT) & mask; - if (index == 0) - return NULL; - - uint32_t offset = OFFSETOF__CORINFO_Continuation__data + (index - 1) * TARGET_POINTER_SIZE; - PTR_BYTE address = dac_cast(dac_cast(this) + offset); - return dac_cast(address); - } - -#ifndef DACCESS_COMPILE - int32_t* GetFlagsAddress() - { - LIMITED_METHOD_CONTRACT; - return (int32_t*)&Flags; - } -#endif // DACCESS_COMPILE - -private: - // README: - // If you modify the order of these fields, make sure to update the definition in - // BCL for this object. - - CONTINUATIONREF Next; - void* ResumeInfo; - int32_t Flags; - int32_t State; -}; - -// This class corresponds to Exception on the managed side. -typedef DPTR(class ExceptionObject) PTR_ExceptionObject; -#include "pshpack4.h" -class ExceptionObject : public Object -{ - friend class CoreLibBinder; - -public: - void SetHResult(HRESULT hr) - { - LIMITED_METHOD_CONTRACT; - _HResult = hr; - } - - HRESULT GetHResult() - { - LIMITED_METHOD_CONTRACT; - return _HResult; - } - - void SetXCode(DWORD code) - { - LIMITED_METHOD_CONTRACT; - _xcode = code; - } - - DWORD GetXCode() - { - LIMITED_METHOD_CONTRACT; - return _xcode; - } - - void SetXPtrs(void* xptrs) - { - LIMITED_METHOD_CONTRACT; - _xptrs = xptrs; - } - - void* GetXPtrs() - { - LIMITED_METHOD_CONTRACT; - return _xptrs; - } - - void SetStackTrace(OBJECTREF stackTrace); - - void GetStackTrace(StackTraceArray & stackTrace, PTRARRAYREF * outKeepaliveArray = NULL) const - { -#ifdef DACCESS_COMPILE - return GetStackTrace(stackTrace, outKeepaliveArray, NULL); -#else - return GetStackTrace(stackTrace, outKeepaliveArray, GetThread()); -#endif // DACCESS_COMPILE - } - -private: - static void GetStackTraceClone(StackTraceArray & stackTrace, PTRARRAYREF * outKeepAliveArray); -public: - void GetStackTrace(StackTraceArray & stackTrace, PTRARRAYREF * outKeepaliveArray, Thread *pCurrentThread) const; - - static void GetStackTraceParts(OBJECTREF stackTraceObj, StackTraceArray & stackTrace, PTRARRAYREF * outKeepaliveArray); - - OBJECTREF GetStackTraceArrayObject() const - { - LIMITED_METHOD_DAC_CONTRACT; - return _stackTrace; - } - - void SetInnerException(OBJECTREF innerException) - { - WRAPPER_NO_CONTRACT; - SetObjectReference((OBJECTREF*)&_innerException, (OBJECTREF)innerException); - } - - OBJECTREF GetInnerException() - { - LIMITED_METHOD_DAC_CONTRACT; - return VolatileLoadWithoutBarrierOBJECTREF(&_innerException); - } - - // Returns the innermost exception object - equivalent of the - // managed System.Exception.GetBaseException method. - OBJECTREF GetBaseException() - { - LIMITED_METHOD_CONTRACT; - - // Loop and get the innermost exception object - OBJECTREF oInnerMostException = NULL; - OBJECTREF oCurrent = NULL; - - oCurrent = GetInnerException(); - while(oCurrent != NULL) - { - oInnerMostException = oCurrent; - oCurrent = ((ExceptionObject*)(Object *)OBJECTREFToObject(oCurrent))->GetInnerException(); - } - - // return the innermost exception - return oInnerMostException; - } - - void SetMessage(STRINGREF message) - { - WRAPPER_NO_CONTRACT; - SetObjectReference((OBJECTREF*)&_message, (OBJECTREF)message); - } - - STRINGREF GetMessage() - { - LIMITED_METHOD_DAC_CONTRACT; - return _message; - } - - void SetStackTraceString(STRINGREF stackTraceString) - { - WRAPPER_NO_CONTRACT; - SetObjectReference((OBJECTREF*)&_stackTraceString, (OBJECTREF)stackTraceString); - } - - STRINGREF GetStackTraceString() - { - LIMITED_METHOD_DAC_CONTRACT; - return _stackTraceString; - } - - STRINGREF GetRemoteStackTraceString() - { - LIMITED_METHOD_DAC_CONTRACT; - return (STRINGREF)VolatileLoadWithoutBarrierOBJECTREF(&_remoteStackTraceString); - } - - void SetHelpURL(STRINGREF helpURL) - { - WRAPPER_NO_CONTRACT; - SetObjectReference((OBJECTREF*)&_helpURL, (OBJECTREF)helpURL); - } - - void SetSource(STRINGREF source) - { - WRAPPER_NO_CONTRACT; - SetObjectReference((OBJECTREF*)&_source, (OBJECTREF)source); - } - - void ClearStackTraceForThrow() - { - WRAPPER_NO_CONTRACT; - SetObjectReference((OBJECTREF*)&_remoteStackTraceString, NULL); - SetObjectReference((OBJECTREF*)&_stackTrace, NULL); - SetObjectReference((OBJECTREF*)&_stackTraceString, NULL); - } - - void ClearStackTracePreservingRemoteStackTrace() - { - WRAPPER_NO_CONTRACT; - SetObjectReference((OBJECTREF*)&_stackTrace, NULL); - SetObjectReference((OBJECTREF*)&_stackTraceString, NULL); - } - - // This method will set the reference to the array - // containing the watson bucket information (in byte[] form). - void SetWatsonBucketReference(OBJECTREF oWatsonBucketArray) - { - WRAPPER_NO_CONTRACT; - SetObjectReference((OBJECTREF*)&_watsonBuckets, (OBJECTREF)oWatsonBucketArray); - } - - // This method will return the reference to the array - // containing the watson buckets - U1ARRAYREF GetWatsonBucketReference() - { - LIMITED_METHOD_CONTRACT; - return (U1ARRAYREF)VolatileLoadWithoutBarrierOBJECTREF(&_watsonBuckets); - } - - // This method will return a BOOL to indicate if the - // watson buckets are present or not. - BOOL AreWatsonBucketsPresent() - { - LIMITED_METHOD_CONTRACT; - return (GetWatsonBucketReference() != NULL)?TRUE:FALSE; - } - - // This method will save the IP to be used for watson bucketing. - void SetIPForWatsonBuckets(UINT_PTR ip) - { - LIMITED_METHOD_CONTRACT; - - _ipForWatsonBuckets = ip; - } - - // This method will return a BOOL to indicate if Watson bucketing IP - // is present (or not). - BOOL IsIPForWatsonBucketsPresent() - { - LIMITED_METHOD_CONTRACT; - - return (_ipForWatsonBuckets != 0); - } - - // This method returns the IP for Watson Buckets. - UINT_PTR GetIPForWatsonBuckets() - { - LIMITED_METHOD_CONTRACT; - - return VolatileLoadWithoutBarrier(&_ipForWatsonBuckets); - } - - // README: - // If you modify the order of these fields, make sure to update the definition in - // BCL for this object. -private: - OBJECTREF _exceptionMethod; //Needed for serialization. - STRINGREF _message; - OBJECTREF _data; - OBJECTREF _innerException; - STRINGREF _helpURL; - OBJECTREF _stackTrace; - U1ARRAYREF _watsonBuckets; - STRINGREF _stackTraceString; //Needed for serialization. - STRINGREF _remoteStackTraceString; - STRINGREF _source; // Mainly used by VB. - - UINT_PTR _ipForWatsonBuckets; // Contains the IP of exception for watson bucketing - void* _xptrs; - INT32 _xcode; - INT32 _HResult; - - friend struct ::cdac_data; -}; - -template<> -struct cdac_data -{ - static constexpr size_t _message = offsetof(ExceptionObject, _message); - static constexpr size_t _innerException = offsetof(ExceptionObject, _innerException); - static constexpr size_t _stackTrace = offsetof(ExceptionObject, _stackTrace); - static constexpr size_t _watsonBuckets = offsetof(ExceptionObject, _watsonBuckets); - static constexpr size_t _stackTraceString = offsetof(ExceptionObject, _stackTraceString); - static constexpr size_t _remoteStackTraceString = offsetof(ExceptionObject, _remoteStackTraceString); - static constexpr size_t _HResult = offsetof(ExceptionObject, _HResult); - static constexpr size_t _xcode = offsetof(ExceptionObject, _xcode); -}; - -#include "poppack.h" - -//=============================================================================== -// #NullableFeature -// #NullableArchitecture -// -// In a nutshell it is counterintuitive to have a boxed Nullable, since a boxed -// object already has a representation for null (the null pointer), and having -// multiple representations for the 'not present' value just causes grief. Thus the -// feature is build make Nullable box to a boxed (not boxed). -// -// We want to do this in a way that does not impact the perf of the runtime in the -// non-nullable case. -// -// To do this we need to -// * Make a new helper for the Unbox case (see code:JIT_Unbox_Nullable) -// * Plumb the JIT to ask for what kind of Boxing helper is needed -// (see code:CEEInfo.getBoxHelper, code:CEEInfo.getUnBoxHelper -// * change all the places in the CLR where we box or unbox by hand, and force -// them to use code:MethodTable.Box, and code:MethodTable.Unbox which in -// turn call code:Nullable.Box and code:Nullable.UnBox, most of these -// are in reflection, and remoting (passing and returning value types). -// -// #NullableVerification -// -// Sadly, the IL Verifier also needs to know about this change. Basically the 'box' -// instruction returns a boxed(T) (not a boxed(Nullable)) for the purposes of -// verfication. The JIT finds out what box returns by calling back to the EE with -// the code:CEEInfo.getTypeForBox API. -// -// #NullableDebugging -// -// Sadly, because the debugger also does its own boxing 'by hand' for expression -// evaluation inside visual studio, it measn that debuggers also need to be aware -// of the fact that Nullable boxes to a boxed. It is the responcibility of -// debuggers to follow this convention (which is why this is sad). -// - -//=============================================================================== -// Nullable represents the managed generic value type Nullable -// -// The runtime has special logic for this value class. When it is boxed -// it becomes either null or a boxed T. Similarly a boxed T can be unboxed -// either as a T (as normal), or as a Nullable -// -// See code:Nullable#NullableArchitecture for more. -// -class Nullable { - Nullable(); // This is purposefully undefined. Do not make instances - // of this class. -public: - static void CheckFieldOffsets(TypeHandle nullableType); - static BOOL IsNullableType(TypeHandle nullableType); - static BOOL IsNullableForType(TypeHandle nullableType, MethodTable* paramMT); - - static OBJECTREF Box(void* src, MethodTable* nullable); - static BOOL UnBox(void* dest, OBJECTREF boxedVal, MethodTable* destMT); - static void UnBoxNoCheck(void* dest, OBJECTREF boxedVal, MethodTable* destMT); - static OBJECTREF BoxedNullableNull(TypeHandle nullableType) { return NULL; } - // if 'Obj' is a true boxed nullable, return the form we want (either null or a boxed T) - static OBJECTREF NormalizeBox(OBJECTREF obj); - - static inline CLR_BOOL HasValue(void *src, MethodTable *nullableMT) - { - Nullable *nullable = (Nullable *)src; - return *(nullable->HasValueAddr(nullableMT)); - } - - static inline void *Value(void *src, MethodTable *nullableMT) - { - Nullable *nullable = (Nullable *)src; - return nullable->ValueAddr(nullableMT); - } - - static int32_t GetValueAddrOffset(MethodTable* nullableMT); - -private: - static BOOL IsNullableForTypeHelper(MethodTable* nullableMT, MethodTable* paramMT); - - CLR_BOOL* HasValueAddr(MethodTable* nullableMT); - void* ValueAddr(MethodTable* nullableMT); -}; - -#ifdef USE_CHECKED_OBJECTREFS -typedef REF EXCEPTIONREF; -#else // USE_CHECKED_OBJECTREFS -typedef PTR_ExceptionObject EXCEPTIONREF; -#endif // USE_CHECKED_OBJECTREFS - -#endif // _OBJECT_H_ From 96df46370ee9720a4a822843ad39899b7741bb36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Wed, 17 Jun 2026 03:12:28 +0200 Subject: [PATCH 30/77] Refactor delegate marker definitions in comdelegate.cpp --- src/coreclr/vm/comdelegate.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 98fec92198aeef..306a66b5d847c0 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -26,8 +26,7 @@ #include "comcallablewrapper.h" #endif // FEATURE_COMINTEROP -#define DELEGATE_MARKER_STATICCLOSEDSPECIAL (-1) -#define DELEGATE_MARKER_UNMANAGEDFPTR (-2) +#define DELEGATE_MARKER_UNMANAGEDFPTR (-1) #ifndef DACCESS_COMPILE From efc644e678ba7ec0f1aac350b81fe263b6946ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Wed, 17 Jun 2026 04:24:52 +0200 Subject: [PATCH 31/77] Revert renames --- .../src/System/Delegate.CoreCLR.cs | 45 +++++----- .../src/System/MulticastDelegate.CoreCLR.cs | 86 +++++++++---------- src/coreclr/debug/daccess/dacdbiimpl.cpp | 8 +- src/coreclr/debug/ee/controller.cpp | 2 +- src/coreclr/jit/flowgraph.cpp | 6 +- .../tools/Common/JitInterface/CorInfoImpl.cs | 4 +- src/coreclr/vm/amd64/asmconstants.h | 6 +- src/coreclr/vm/arm/stubs.cpp | 4 +- src/coreclr/vm/arm64/stubs.cpp | 6 +- src/coreclr/vm/comdelegate.cpp | 71 ++++++++------- src/coreclr/vm/corelib.h | 30 +++---- src/coreclr/vm/dllimport.cpp | 4 +- src/coreclr/vm/dllimportcallback.h | 2 +- src/coreclr/vm/i386/stublinkerx86.cpp | 8 +- src/coreclr/vm/i386/virtualcallstubcpu.hpp | 2 +- src/coreclr/vm/interpexec.cpp | 6 +- src/coreclr/vm/jithelpers.cpp | 8 +- src/coreclr/vm/jitinterface.cpp | 14 +-- src/coreclr/vm/loongarch64/stubs.cpp | 6 +- src/coreclr/vm/object.h | 49 ++++++----- src/coreclr/vm/riscv64/stubs.cpp | 4 +- src/coreclr/vm/stubhelpers.cpp | 4 +- src/coreclr/vm/stubmgr.cpp | 12 +-- src/coreclr/vm/virtualcallstub.cpp | 2 +- 24 files changed, 192 insertions(+), 197 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 0cc5ee6bde1877..98b060926b1270 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; -using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; @@ -23,21 +22,21 @@ public abstract partial class Delegate : ICloneable, ISerializable // 2. Wrapper delegate // 3. Method cache // 4. Collectible delegates - internal object? _helperObject; // Initialized by VM as needed + internal object? _invocationList; // Initialized by VM as needed // _firstParameter is the object we will invoke on; null if static delegate - // Keep _firstParameter and _functionPointer next to each other for optimal delegate invoke performance + // Keep _firstParameter and _methodPtr next to each other for optimal delegate invoke performance internal object? _firstParameter; - // _functionPointer is a pointer to the method we will invoke + // _methodPtr is a pointer to the method we will invoke // It could be a small thunk if this is a static or UM call - internal nuint _functionPointer; + internal nuint _methodPtr; // In the case of a static method passed to a delegate, this field stores - // whatever _functionPointer would have stored: and _functionPointer points to a + // whatever _methodPtr would have stored: and _methodPtr points to a // small thunk which removes the "this" pointer before going on - // to _functionPointer. - internal nint _extraFunctionPointerOrData; + // to _methodPtr. + internal nint _methodPtrAux; internal nint _invocationCount; @@ -57,13 +56,13 @@ private nuint MethodDesc internal bool IsUnmanagedFunctionPtr => _invocationCount == -1; - internal bool InvocationListLogicallyNull => _helperObject is not (object[] or Delegate); + internal bool InvocationListLogicallyNull => _invocationList is not (object[] or Delegate); private object[] GetObjectArray() { Debug.Assert(IsSpecialDelegate); - object[]? invocationList = Unsafe.AsAssert(_helperObject); + object[]? invocationList = Unsafe.AsAssert(_invocationList); Debug.Assert(invocationList is not null); Debug.Assert(invocationList.Length > 1); @@ -148,8 +147,8 @@ public override bool Equals([NotNullWhen(true)] object? obj) // do an optimistic check first. This is hopefully cheap enough to be worth if (_firstParameter == other._firstParameter && - _functionPointer == other._functionPointer && - _extraFunctionPointerOrData == other._extraFunctionPointerOrData) + _methodPtr == other._methodPtr && + _methodPtrAux == other._methodPtrAux) return true; // even though the fields were not all equals the delegates may still match @@ -158,9 +157,9 @@ public override bool Equals([NotNullWhen(true)] object? obj) // It may also happen that the method pointer was not jitted when creating one delegate and jitted in the other // if that's the case the delegates may still be equals but we need to make a more complicated check - if (_extraFunctionPointerOrData == 0) + if (_methodPtrAux == 0) { - if (other._extraFunctionPointerOrData != 0) + if (other._methodPtrAux != 0) return false; // different delegate kind // they are both closed over the first arg @@ -171,12 +170,12 @@ public override bool Equals([NotNullWhen(true)] object? obj) } else { - if (other._extraFunctionPointerOrData == 0) + if (other._methodPtrAux == 0) return false; // different delegate kind // Ignore the target as it will be the delegate instance, though it may be a different one - if (_extraFunctionPointerOrData == other._extraFunctionPointerOrData) + if (_methodPtrAux == other._methodPtrAux) return true; // fall through method handle check @@ -188,7 +187,7 @@ public override bool Equals([NotNullWhen(true)] object? obj) public override int GetHashCode() { int hashCode = MethodDesc.GetHashCode(); - if (_extraFunctionPointerOrData == 0 && _firstParameter != null) + if (_methodPtrAux == 0 && _firstParameter != null) { hashCode += RuntimeHelpers.GetHashCode(_firstParameter) * 33; } @@ -197,12 +196,12 @@ public override int GetHashCode() internal virtual object? GetTarget() { - return _extraFunctionPointerOrData == 0 ? _firstParameter : null; + return _methodPtrAux == 0 ? _firstParameter : null; } protected virtual MethodInfo GetMethodImpl() { - return _helperObject switch + return _invocationList switch { MethodInfo methodInfo => methodInfo, object[] => GetLastInvocationUnchecked().Method, @@ -218,7 +217,7 @@ MethodInfo GetMethodImplUncached() MethodInfo method = GetMethodImplCore(); Debug.Assert(method is not null); - Volatile.Write(ref _helperObject, method); + Volatile.Write(ref _invocationList, method); return method; } } @@ -250,7 +249,7 @@ private MethodInfo GetMethodImplCore() bool isStatic = (RuntimeMethodHandle.GetAttributes(method) & MethodAttributes.Static) != 0; if (!isStatic) { - if (_extraFunctionPointerOrData == 0) + if (_methodPtrAux == 0) { // The target may be of a derived type that doesn't have visibility onto the // target method. We don't want to call RuntimeType.GetMethodBase below with that @@ -301,13 +300,13 @@ internal void StoreDynamicMethod(MethodInfo dynamicMethod) Debug.Assert(!IsUnmanagedFunctionPtr, "dynamic method and unmanaged fntptr delegate combined"); // must be a secure/wrapper one, unwrap and save - Delegate d = ((Delegate?)_helperObject)!; + Delegate d = ((Delegate?)_invocationList)!; d.StoreDynamicMethod(dynamicMethod); return; } Debug.Assert(InvocationListLogicallyNull, "dynamic method with invocation list"); - _helperObject = dynamicMethod; + _invocationList = dynamicMethod; } public object? Target => GetTarget(); diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index 417b2a9792ea04..a3e087f528ce17 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -4,8 +4,6 @@ using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; @@ -44,11 +42,11 @@ public sealed override bool Equals([NotNullWhen(true)] object? obj) if (IsSpecialDelegate) { // there are 4 kind of delegate kinds that fall into this bucket - // 1- Multicast (_helperObject is Object[]) - // 2- Wrapper (_helperObject is Delegate) - // 3- Unmanaged FntPtr (_helperObject == null) - // 4- Open virtual (_invocationCount == MethodDesc of target, _helperObject == null, LoaderAllocator, or DynamicResolver) - switch (_helperObject) + // 1- Multicast (_invocationList is Object[]) + // 2- Wrapper (_invocationList is Delegate) + // 3- Unmanaged FntPtr (_invocationList == null) + // 4- Open virtual (_invocationCount == MethodDesc of target, _invocationList == null, LoaderAllocator, or DynamicResolver) + switch (_invocationList) { case object[]: return GetInvocationsUnchecked().SequenceEqual(other.GetInvocationsUnchecked()); @@ -59,21 +57,21 @@ public sealed override bool Equals([NotNullWhen(true)] object? obj) if (IsUnmanagedFunctionPtr) { return other.IsUnmanagedFunctionPtr && - _functionPointer == other._functionPointer && - _extraFunctionPointerOrData == other._extraFunctionPointerOrData; + _methodPtr == other._methodPtr && + _methodPtrAux == other._methodPtrAux; } // now we know 'this' is not a special one, so we can work out what the other is // this is a wrapper delegate so we need to unwrap and check the inner one - return other._helperObject is Delegate ? Equals(other._helperObject) : base.Equals(obj); + return other._invocationList is Delegate ? Equals(other._invocationList) : base.Equals(obj); } } Debug.Assert(InvocationListLogicallyNull); // now we know 'this' is not a special one, so we can work out what the other is - return other._helperObject is Delegate ? + return other._invocationList is Delegate ? // this is a wrapper delegate so we need to unwrap and check the inner one - Equals(other._helperObject) : + Equals(other._invocationList) : // we can call on the base base.Equals(other); } @@ -95,8 +93,8 @@ private static bool TrySetSlot(object?[] a, int index, object o) MulticastDelegate d = (MulticastDelegate)o; MulticastDelegate dd = (MulticastDelegate)previous; - return dd._functionPointer == d._functionPointer && - dd._extraFunctionPointerOrData == d._extraFunctionPointerOrData && + return dd._methodPtr == d._methodPtr && + dd._methodPtrAux == d._methodPtrAux && dd._firstParameter == d._firstParameter; } @@ -106,19 +104,19 @@ private unsafe MulticastDelegate NewMulticastDelegate(object[] invocationList, i MulticastDelegate result = Unsafe.AsAssert(RuntimeTypeHandle.InternalAllocNoChecks(RuntimeHelpers.GetMethodTable(this))); // Performance optimization - if this already points to a true multicast delegate, - // copy _functionPointer and _functionPointerAux fields rather than calling into the EE to get them + // copy _methodPtr and _methodPtrAux fields rather than calling into the EE to get them if (thisIsMultiCastAlready) { - result._functionPointer = _functionPointer; - result._extraFunctionPointerOrData = _extraFunctionPointerOrData; + result._methodPtr = _methodPtr; + result._methodPtrAux = _methodPtrAux; } else { - result._functionPointer = GetMulticastInvoke(); - result._extraFunctionPointerOrData = GetInvokeMethod(); + result._methodPtr = GetMulticastInvoke(); + result._methodPtrAux = GetInvokeMethod(); } result._firstParameter = result; - result._helperObject = invocationList; + result._invocationList = invocationList; result._invocationCount = invocationCount; return result; @@ -143,12 +141,12 @@ protected sealed override Delegate CombineImpl(Delegate? follow) MulticastDelegate dFollow = (MulticastDelegate)follow; object[]? resultList; int followCount = 1; - object[]? followList = dFollow._helperObject as object[]; + object[]? followList = dFollow._invocationList as object[]; if (followList != null) followCount = (int)dFollow._invocationCount; int resultCount; - if (_helperObject is not object[] invocationList) + if (_invocationList is not object[] invocationList) { resultCount = 1 + followCount; resultList = new object[resultCount]; @@ -217,7 +215,7 @@ protected sealed override Delegate CombineImpl(Delegate? follow) private object[] DeleteFromInvocationList(object[] invocationList, int invocationCount, int deleteIndex, int deleteCount) { - object[]? thisInvocationList = Unsafe.AsAssert(_helperObject); + object[]? thisInvocationList = Unsafe.AsAssert(_invocationList); Debug.Assert(thisInvocationList is not null); int allocCount = thisInvocationList.Length; @@ -261,11 +259,11 @@ private static bool EqualInvocationLists(object[] a, object[] b, int start, int if (!v.HasSingleTarget) { - if (_helperObject is object[] invocationList) + if (_invocationList is object[] invocationList) { int invocationCount = (int)_invocationCount; int vInvocationCount = (int)v._invocationCount; - object[] vInvocationList = (object[])v._helperObject!; + object[] vInvocationList = (object[])v._invocationList!; for (int i = invocationCount - vInvocationCount; i >= 0; i--) { if (!EqualInvocationLists(invocationList, vInvocationList, i, vInvocationCount)) @@ -292,7 +290,7 @@ private static bool EqualInvocationLists(object[] a, object[] b, int start, int } else { - if (_helperObject is object[] invocationList) + if (_invocationList is object[] invocationList) { int invocationCount = (int)_invocationCount; for (int i = invocationCount; --i >= 0;) @@ -326,7 +324,7 @@ private static bool EqualInvocationLists(object[] a, object[] b, int start, int // This method returns the Invocation list of this multicast delegate. public sealed override Delegate[] GetInvocationList() { - if (_helperObject is not object[]) + if (_invocationList is not object[]) { return [this]; } @@ -339,7 +337,7 @@ public sealed override Delegate[] GetInvocationList() return del; } - internal new bool HasSingleTarget => _helperObject is not object[]; + internal new bool HasSingleTarget => _invocationList is not object[]; // Used by delegate invocation list enumerator internal MulticastDelegate? TryGetAt(int index) @@ -362,11 +360,11 @@ public sealed override Delegate[] GetInvocationList() public sealed override int GetHashCode() { if (IsUnmanagedFunctionPtr) - return HashCode.Combine(_functionPointer, _functionPointer); + return HashCode.Combine(_methodPtr, _methodPtr); if (IsSpecialDelegate) { - switch (_helperObject) + switch (_invocationList) { case object[]: { @@ -402,7 +400,7 @@ public sealed override int GetHashCode() return null; } - switch (_helperObject) + switch (_invocationList) { case object[]: return GetLastInvocationUnchecked().Target; @@ -427,7 +425,7 @@ private void CtorClosed(object target, IntPtr methodPtr) if (target == null) ThrowNullThisInDelegateToInstance(); _firstParameter = target; - _functionPointer = (nuint)methodPtr; + _methodPtr = (nuint)methodPtr; } [DebuggerNonUserCode] @@ -435,7 +433,7 @@ private void CtorClosed(object target, IntPtr methodPtr) private void CtorClosedStatic(object target, IntPtr methodPtr) { _firstParameter = target; - _functionPointer = (nuint)methodPtr; + _methodPtr = (nuint)methodPtr; } [DebuggerNonUserCode] @@ -445,7 +443,7 @@ private void CtorRTClosed(object target, IntPtr methodPtr) if (target == null) ThrowNullThisInDelegateToInstance(); _firstParameter = target; - _functionPointer = (nuint)AdjustTarget(target, methodPtr); + _methodPtr = (nuint)AdjustTarget(target, methodPtr); } [DebuggerNonUserCode] @@ -453,8 +451,8 @@ private void CtorRTClosed(object target, IntPtr methodPtr) private void CtorOpened(object target, IntPtr methodPtr, IntPtr shuffleThunk) { _firstParameter = this; - _functionPointer = (nuint)shuffleThunk; - _extraFunctionPointerOrData = methodPtr; + _methodPtr = (nuint)shuffleThunk; + _methodPtrAux = methodPtr; } [DebuggerNonUserCode] @@ -462,7 +460,7 @@ private void CtorOpened(object target, IntPtr methodPtr, IntPtr shuffleThunk) private void CtorVirtualDispatch(object target, IntPtr methodPtr, IntPtr shuffleThunk) { _firstParameter = this; - _functionPointer = (nuint)shuffleThunk; + _methodPtr = (nuint)shuffleThunk; InitializeVirtualCallStub(methodPtr); } @@ -471,8 +469,8 @@ private void CtorVirtualDispatch(object target, IntPtr methodPtr, IntPtr shuffle private void CtorCollectibleClosedStatic(object target, IntPtr methodPtr, IntPtr gchandle) { _firstParameter = target; - _functionPointer = (nuint)methodPtr; - _helperObject = GCHandle.InternalGet(gchandle); + _methodPtr = (nuint)methodPtr; + _invocationList = GCHandle.InternalGet(gchandle); Debug.Assert(InvocationListLogicallyNull); } @@ -481,9 +479,9 @@ private void CtorCollectibleClosedStatic(object target, IntPtr methodPtr, IntPtr private void CtorCollectibleOpened(object target, IntPtr methodPtr, IntPtr shuffleThunk, IntPtr gchandle) { _firstParameter = this; - _functionPointer = (nuint)shuffleThunk; - _extraFunctionPointerOrData = methodPtr; - _helperObject = GCHandle.InternalGet(gchandle); + _methodPtr = (nuint)shuffleThunk; + _methodPtrAux = methodPtr; + _invocationList = GCHandle.InternalGet(gchandle); Debug.Assert(InvocationListLogicallyNull); } @@ -492,8 +490,8 @@ private void CtorCollectibleOpened(object target, IntPtr methodPtr, IntPtr shuff private void CtorCollectibleVirtualDispatch(object target, IntPtr methodPtr, IntPtr shuffleThunk, IntPtr gchandle) { _firstParameter = this; - _functionPointer = (nuint)shuffleThunk; - _helperObject = GCHandle.InternalGet(gchandle); + _methodPtr = (nuint)shuffleThunk; + _invocationList = GCHandle.InternalGet(gchandle); Debug.Assert(InvocationListLogicallyNull); InitializeVirtualCallStub(methodPtr); } diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index 3e39ae3814e715..d282c4e45ddd84 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -3269,7 +3269,7 @@ DacDbiInterfaceImpl::DelegateType DacDbiInterfaceImpl::GetDelegateType(VMPTR_Obj // If this delegate points to a static function or this is a open virtual delegate, this should be non-null // Special case: This might fail in a VSD delegate (instance open virtual)... // TODO: There is the special signatures cases missing. - TADDR targetMethodPtr = PCODEToPINSTR(pDelObj->GetExtraFunctionPointerOrData()); + TADDR targetMethodPtr = PCODEToPINSTR(pDelObj->GetMethodPtrAux()); if (targetMethodPtr == (TADDR)NULL) { // Static extension methods, other closed static delegates, and instance delegates fall into this category. @@ -3306,10 +3306,10 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetDelegateFunctionData( switch (GetDelegateType(delegateObject)) { case kClosedDelegate: - targetMethodPtr = PCODEToPINSTR(pDelObj->GetFunctionPointer()); + targetMethodPtr = PCODEToPINSTR(pDelObj->GetMethodPtr()); break; case kOpenDelegate: - targetMethodPtr = PCODEToPINSTR(pDelObj->GetExtraFunctionPointerOrData()); + targetMethodPtr = PCODEToPINSTR(pDelObj->GetMethodPtrAux()); break; default: ThrowHR(CORDBG_E_UNSUPPORTED_DELEGATE); @@ -3345,7 +3345,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetDelegateTargetObject( { case kClosedDelegate: { - PTR_Object pRemoteTargetObj = OBJECTREFToObject(pDelObj->GetFirstParameter()); + PTR_Object pRemoteTargetObj = OBJECTREFToObject(pDelObj->GetTarget()); ppTargetObj->SetDacTargetPtr(pRemoteTargetObj.GetAddr()); break; } diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index 57eecbb07ed79d..52a3fbe68d7024 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -8262,7 +8262,7 @@ void DebuggerStepper::TriggerMulticastDelegate(DELEGATEREF pDel, INT32 delegateC TraceDestination trace; FramePointer fp = LEAF_MOST_FRAME; - PTRARRAYREF pDelInvocationList = (PTRARRAYREF) pDel->GetHelperObject(); + PTRARRAYREF pDelInvocationList = (PTRARRAYREF) pDel->GetInvocationList(); DELEGATEREF pCurrentInvokeDel = (DELEGATEREF) pDelInvocationList->GetAt(delegateCount); StubLinkStubManager::TraceDelegateObject((BYTE*)OBJECTREFToObject(pCurrentInvokeDel), &trace); diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index c8b6019f5eb326..3f09ac4fba8406 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -1183,15 +1183,15 @@ GenTree* Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call, #endif if (targetMethodHnd != nullptr) { + CORINFO_METHOD_HANDLE alternateCtor = nullptr; DelegateCtorArgs ctorData; ctorData.pMethod = info.compMethodHnd; ctorData.pArg3 = nullptr; ctorData.pArg4 = nullptr; ctorData.pArg5 = nullptr; - CORINFO_METHOD_HANDLE alternateCtor = - info.compCompHnd->GetDelegateCtor(methHnd, clsHnd, targetMethodHnd, &ctorData); - if (alternateCtor != NO_METHOD_HANDLE && alternateCtor != methHnd) + alternateCtor = info.compCompHnd->GetDelegateCtor(methHnd, clsHnd, targetMethodHnd, &ctorData); + if (alternateCtor != methHnd) { JITDUMP("optimized\n"); // we erase any inline info that may have been set for generics has it is not needed here, diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 0ae50a3cae4277..e11a597e10ce28 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3441,9 +3441,9 @@ private void getEEInfo(ref CORINFO_EE_INFO pEEInfoOut) pEEInfoOut.inlinedCallFrameInfo.size = (uint)SizeOfPInvokeTransitionFrame; - // _firstParameter + // _target/_firstParameter pEEInfoOut.offsetOfDelegateInstance = 2 * (uint)pointerSize; - // _functionPointer + // _methodPtr/_functionPointer pEEInfoOut.offsetOfDelegateFirstTarget = pEEInfoOut.offsetOfDelegateInstance + (uint)pointerSize; pEEInfoOut.sizeOfReversePInvokeFrame = (uint)SizeOfReversePInvokeTransitionFrame; diff --git a/src/coreclr/vm/amd64/asmconstants.h b/src/coreclr/vm/amd64/asmconstants.h index 26a29a1974fac6..f6507886d6bda0 100644 --- a/src/coreclr/vm/amd64/asmconstants.h +++ b/src/coreclr/vm/amd64/asmconstants.h @@ -443,9 +443,9 @@ ASMCONSTANTS_C_ASSERT(OFFSET__TEB__ThreadLocalStoragePointer == offsetof(TEB, Th #define Thread__ObjectRefFlush ?ObjectRefFlush@Thread@@SAXPEAV1@@Z -#define DELEGATE_FIELD_OFFSET__EXTRA_FUNCTION_POINTER_OR_DATA 0x20 -ASMCONSTANTS_RUNTIME_ASSERT(DELEGATE_FIELD_OFFSET__EXTRA_FUNCTION_POINTER_OR_DATA == Object::GetOffsetOfFirstField() + - CoreLibBinder::GetFieldOffset(FIELD__DELEGATE__EXTRA_FUNCTION_POINTER_OR_DATA)); +#define DELEGATE_FIELD_OFFSET__METHOD_AUX 0x20 +ASMCONSTANTS_RUNTIME_ASSERT(DELEGATE_FIELD_OFFSET__METHOD_AUX == Object::GetOffsetOfFirstField() + + CoreLibBinder::GetFieldOffset(FIELD__DELEGATE__METHOD_PTR_AUX)); #define MAX_STRING_LENGTH 0x3FFFFFDF diff --git a/src/coreclr/vm/arm/stubs.cpp b/src/coreclr/vm/arm/stubs.cpp index 298b8823e5497b..815ab68867a005 100644 --- a/src/coreclr/vm/arm/stubs.cpp +++ b/src/coreclr/vm/arm/stubs.cpp @@ -939,7 +939,7 @@ VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) // On entry r0 holds the delegate instance. Look up the real target address stored in the MethodPtrAux // field and stash it in r12. // ldr r12, [r0, #offsetof(DelegateObject, _methodPtrAux)] - ThumbEmitLoadRegIndirect(ThumbReg(12), ThumbReg(0), DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); + ThumbEmitLoadRegIndirect(ThumbReg(12), ThumbReg(0), DelegateObject::GetOffsetOfMethodPtrAux()); // Emit the instructions to rewrite the argument registers. Most will be register-to-register (e.g. // move r1 to r0) but one or two of them might move values from the top of the incoming stack @@ -997,7 +997,7 @@ VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) // On entry r0 holds the delegate instance. Look up the real target address stored in the MethodPtrAux // field and stash it in r12. // ldr r12, [r0, #offsetof(DelegateObject, _methodPtrAux)] - ThumbEmitLoadRegIndirect(ThumbReg(12), ThumbReg(0), DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); + ThumbEmitLoadRegIndirect(ThumbReg(12), ThumbReg(0), DelegateObject::GetOffsetOfMethodPtrAux()); // As we copy slots from lower in the argument stack to higher we need to keep track of source and // destination pointers into those arguments (if we just use offsets from SP we get into trouble with diff --git a/src/coreclr/vm/arm64/stubs.cpp b/src/coreclr/vm/arm64/stubs.cpp index 19a7602c608703..ba9a29ff4bcde5 100644 --- a/src/coreclr/vm/arm64/stubs.cpp +++ b/src/coreclr/vm/arm64/stubs.cpp @@ -783,9 +783,9 @@ VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) // On entry x0 holds the delegate instance. Look up the real target address stored in the MethodPtrAux // field and save it in x16(ip). Tailcall to the target method after re-arranging the arguments // ldr x16, [x0, #offsetof(DelegateObject, _methodPtrAux)] - EmitLoadStoreRegImm(eLOAD, IntReg(16), IntReg(0), DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); - //add x11, x0, DelegateObject::GetOffsetOfExtraFunctionPointerOrData() - load the indirection cell into x11 used by ResolveWorkerAsmStub - EmitAddImm(IntReg(11), IntReg(0), DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); + EmitLoadStoreRegImm(eLOAD, IntReg(16), IntReg(0), DelegateObject::GetOffsetOfMethodPtrAux()); + //add x11, x0, DelegateObject::GetOffsetOfMethodPtrAux() - load the indirection cell into x11 used by ResolveWorkerAsmStub + EmitAddImm(IntReg(11), IntReg(0), DelegateObject::GetOffsetOfMethodPtrAux()); for (ShuffleEntry* pEntry = pShuffleEntryArray; pEntry->srcofs != ShuffleEntry::SENTINEL; pEntry++) { diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 306a66b5d847c0..6676d6fe03a9e7 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -852,7 +852,7 @@ static Stub* CreateILDelegateShuffleThunk(MethodDesc* pDelegateMD, bool callTarg pCode->EmitLDARG(i); pCode->EmitLoadThis(); - pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__EXTRA_FUNCTION_POINTER_OR_DATA))); + pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__METHOD_PTR_AUX))); pCode->EmitCALLI(TOKEN_ILSTUB_TARGET_SIG, sig.NumFixedArgs(), sig.IsReturnTypeVoid() ? 0 : 1); pCode->EmitRET(); @@ -1218,7 +1218,7 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, // Open delegates use themselves as the target (which handily allows their shuffle thunks to locate additional data at // invocation time). - refRealDelegate->SetFirstParameter(refRealDelegate); + refRealDelegate->SetTarget(refRealDelegate); // We need to shuffle arguments for open delegates since the first argument on the calling side is not meaningful to the // callee. @@ -1226,7 +1226,7 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, PCODE pEntryPoint = SetupShuffleThunk(pDelegateMT, pTargetMethod); // Indicate that the delegate will jump to the shuffle thunk rather than directly to the target method. - refRealDelegate->SetFunctionPointer(pEntryPoint); + refRealDelegate->SetMethodPtr(pEntryPoint); // Use stub dispatch for all virtuals. // Investigate not using this for non-interface virtuals. @@ -1240,7 +1240,7 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, // needs to jump to another stub (this time provided by the VirtualStubManager) that will virtualize the call at // runtime. PCODE pTargetCall = GetVirtualCallStub(pTargetMethod, TypeHandle(pExactMethodType)); - refRealDelegate->SetExtraFunctionPointerOrData(pTargetCall); + refRealDelegate->SetMethodPtrAux(pTargetCall); refRealDelegate->SetInvocationCount((INT_PTR)(void *)pTargetMethod); } else @@ -1266,7 +1266,7 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, // Note that it is important to cache pTargetCode in local variable to avoid GC hole. // GetMultiCallableAddrOfCode() can trigger GC. PCODE pTargetCode = pTargetMethod->GetMultiCallableAddrOfCode(); - refRealDelegate->SetExtraFunctionPointerOrData(pTargetCode); + refRealDelegate->SetMethodPtrAux(pTargetCode); } } else @@ -1297,15 +1297,15 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, } _ASSERTE(pTargetCode); - refRealDelegate->SetFirstParameter(*pRefFirstArg); - refRealDelegate->SetFunctionPointer(pTargetCode); + refRealDelegate->SetTarget(*pRefFirstArg); + refRealDelegate->SetMethodPtr(pTargetCode); } LoaderAllocator *pLoaderAllocator = pTargetMethod->GetLoaderAllocator(); - _ASSERTE(refRealDelegate->GetHelperObject() == NULL); + _ASSERTE(refRealDelegate->GetInvocationList() == NULL); if (pLoaderAllocator->IsCollectible()) - refRealDelegate->SetHelperObject(pLoaderAllocator->GetExposedObject()); + refRealDelegate->SetInvocationList(pLoaderAllocator->GetExposedObject()); GCPROTECT_END(); } @@ -1341,7 +1341,7 @@ LPVOID COMDelegate::ConvertToCallback(OBJECTREF pDelegateObj) // that function pointer. if (DELEGATE_MARKER_UNMANAGEDFPTR == pDelegate->GetInvocationCount()) { - pCode = pDelegate->GetExtraFunctionPointerOrData(); + pCode = pDelegate->GetMethodPtrAux(); } else { @@ -1389,7 +1389,7 @@ LPVOID COMDelegate::ConvertToCallback(OBJECTREF pDelegateObj) _ASSERTE(objhnd != NULL); // This target should not ever be used. We are storing it in the thunk for better diagnostics of "call on collected delegate" crashes. - PCODE pManagedTargetForDiagnostics = pDelegate->GetExtraFunctionPointerOrData() != (PCODE)NULL ? pDelegate->GetExtraFunctionPointerOrData() : pDelegate->GetFunctionPointer(); + PCODE pManagedTargetForDiagnostics = pDelegate->GetMethodPtrAux() != (PCODE)NULL ? pDelegate->GetMethodPtrAux() : pDelegate->GetMethodPtr(); // MethodDesc is passed in for profiling to know the method desc of target pUMEntryThunk->LoadTimeInit( @@ -1517,11 +1517,11 @@ OBJECTREF COMDelegate::ConvertToDelegate(LPVOID pCallback, MethodTable* pMT) GCX_NOTRIGGER(); // Wire up the unmanaged call stub to the delegate. - delObj->SetFirstParameter(delObj); // We are the "this" object + delObj->SetTarget(delObj); // We are the "this" object // For X86, we save the entry point in the delegate's method pointer and the UM Callsite in the aux pointer. - delObj->SetFunctionPointer(pMarshalStub); - delObj->SetExtraFunctionPointerOrData((PCODE)pCallback); + delObj->SetMethodPtr(pMarshalStub); + delObj->SetMethodPtrAux((PCODE)pCallback); // Also, mark this delegate as an unmanaged function pointer wrapper. delObj->SetInvocationCount(DELEGATE_MARKER_UNMANAGEDFPTR); @@ -1597,7 +1597,7 @@ extern "C" void QCALLTYPE Delegate_InitializeVirtualCallStub(QCall::ObjectHandle PCODE target = GetVirtualCallStub(pMeth, TypeHandle(pMeth->GetMethodTable())); DELEGATEREF refThis = (DELEGATEREF)d.Get(); - refThis->SetExtraFunctionPointerOrData(target); + refThis->SetMethodPtrAux(target); refThis->SetInvocationCount((INT_PTR)(void*)pMeth); END_QCALL; @@ -1729,30 +1729,30 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q if (COMDelegate::NeedsWrapperDelegate(pMeth)) refThis = COMDelegate::CreateWrapperDelegate(refThis, pMeth); - _ASSERTE(refThis->GetHelperObject() == NULL); + _ASSERTE(refThis->GetInvocationList() == NULL); if (pMeth->GetLoaderAllocator()->IsCollectible()) - refThis->SetHelperObject(pMeth->GetLoaderAllocator()->GetExposedObject()); + refThis->SetInvocationList(pMeth->GetLoaderAllocator()->GetExposedObject()); // Open delegates. if (invokeArgCount == methodArgCount) { // set the target - refThis->SetFirstParameter(refThis); + refThis->SetTarget(refThis); // set the shuffle thunk PCODE pEntryPoint = SetupShuffleThunk(pDelMT, pMeth); - refThis->SetFunctionPointer(pEntryPoint); + refThis->SetMethodPtr(pEntryPoint); // set the ptr aux according to what is needed, if virtual need to call make virtual stub dispatch if (!pMeth->IsStatic() && pMeth->IsVirtual() && !pMeth->GetMethodTable()->IsValueType()) { PCODE pTargetCall = GetVirtualCallStub(pMeth, TypeHandle(pMeth->GetMethodTable())); - refThis->SetExtraFunctionPointerOrData(pTargetCall); + refThis->SetMethodPtrAux(pTargetCall); refThis->SetInvocationCount((INT_PTR)(void *)pMeth); } else { - refThis->SetExtraFunctionPointerOrData(method); + refThis->SetMethodPtrAux(method); } } else @@ -1801,8 +1801,8 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q } #endif // HAS_THISPTR_RETBUF_PRECODE - refThis->SetFirstParameter(target.Get()); - refThis->SetFunctionPointer((PCODE)(void *)method); + refThis->SetTarget(target.Get()); + refThis->SetMethodPtr((PCODE)(void *)method); } GCPROTECT_END(); @@ -1855,7 +1855,7 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) // in the wrapper delegate case we want to unwrap and return the method desc of the inner delegate // in the other cases we return the method desc for the invoke bool fOpenVirtualDelegate = false; - OBJECTREF innerDel = thisDel->GetHelperObject(); + OBJECTREF innerDel = thisDel->GetInvocationList(); if (innerDel != NULL) { MethodTable *pMT = innerDel->GetMethodTable(); @@ -1884,12 +1884,12 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) else { // Next, check for an open delegate - PCODE code = thisDel->GetExtraFunctionPointerOrData(); + PCODE code = thisDel->GetMethodPtrAux(); if (code == (PCODE)NULL) { // Must be a normal delegate - code = thisDel->GetFunctionPointer(); + code = thisDel->GetMethodPtr(); } pMethodHandle = NonVirtualEntry2MethodDesc(code); @@ -1915,7 +1915,6 @@ OBJECTREF COMDelegate::GetTargetObject(OBJECTREF obj) OBJECTREF targetObject = NULL; DELEGATEREF thisDel = (DELEGATEREF) obj; - OBJECTREF innerDel = NULL; if (thisDel->GetInvocationCount() != 0) { @@ -1926,7 +1925,7 @@ OBJECTREF COMDelegate::GetTargetObject(OBJECTREF obj) // - virtual delegate - _helperObject == null && _invocationCount == (target MethodDesc) // or _helperObject points to a LoaderAllocator/DynamicResolver (inner open virtual delegate of a Wrapper Delegate) // in the wrapper delegate case we want to unwrap and return the object of the inner delegate - innerDel = thisDel->GetHelperObject(); + OBJECTREF innerDel = thisDel->GetInvocationList(); if (innerDel != NULL) { MethodTable *pMT = innerDel->GetMethodTable(); @@ -1938,7 +1937,7 @@ OBJECTREF COMDelegate::GetTargetObject(OBJECTREF obj) } if (targetObject == NULL) - targetObject = thisDel->GetFirstParameter(); + targetObject = thisDel->GetTarget(); return targetObject; } @@ -1958,7 +1957,7 @@ BOOL COMDelegate::IsTrueMulticastDelegate(OBJECTREF delegate) size_t invocationCount = ((DELEGATEREF)delegate)->GetInvocationCount(); if (invocationCount) { - OBJECTREF invocationList = ((DELEGATEREF)delegate)->GetHelperObject(); + OBJECTREF invocationList = ((DELEGATEREF)delegate)->GetInvocationList(); if (invocationList != NULL) { MethodTable *pMT = invocationList->GetMethodTable(); @@ -2113,17 +2112,17 @@ DELEGATEREF COMDelegate::CreateWrapperDelegate(DELEGATEREF delegate, MethodDesc* // // Object reference field... - gc.refWrapperDel->SetFirstParameter(gc.refWrapperDel); + gc.refWrapperDel->SetTarget(gc.refWrapperDel); // save the secure invoke stub. GetWrapperInvoke() can trigger GC. PCODE tmp = GetWrapperInvoke(pMD); - gc.refWrapperDel->SetFunctionPointer(tmp); + gc.refWrapperDel->SetMethodPtr(tmp); // save the delegate MethodDesc for the frame gc.refWrapperDel->SetInvocationCount((INT_PTR)pMD); // save the delegate to forward to gc.innerDel = (DELEGATEREF) pDelegateType->Allocate(); - gc.refWrapperDel->SetHelperObject(gc.innerDel); + gc.refWrapperDel->SetInvocationList(gc.innerDel); GCPROTECT_END(); @@ -2241,7 +2240,7 @@ extern "C" PCODE QCALLTYPE Delegate_GetMulticastInvokeSlow(MethodTable* pDelegat // Load next delegate from array using LoopCounter as index pCode->EmitLoadThis(); - pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__HELPER_OBJECT))); + pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__INVOCATION_LIST))); pCode->EmitLDLOC(dwLoopCounterNum); pCode->EmitLDELEM_REF(); @@ -2337,7 +2336,7 @@ PCODE COMDelegate::GetWrapperInvoke(MethodDesc* pMD) // Load the "real" delegate pCode->EmitLoadThis(); - pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__HELPER_OBJECT))); + pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__INVOCATION_LIST))); // Load the arguments UINT paramCount = 0; @@ -2977,7 +2976,7 @@ BOOL COMDelegate::IsWrapperDelegate(DELEGATEREF dRef) CONTRACTL_END; if (dRef->GetInvocationCount() != 0) { - OBJECTREF innerDel = dRef->GetHelperObject(); + OBJECTREF innerDel = dRef->GetInvocationList(); if (innerDel != NULL && innerDel->GetMethodTable()->IsDelegate()) { // We have a wrapper delegate diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index dc7a5ff42e67bb..6b230e60557374 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -227,21 +227,21 @@ DEFINE_METHOD(DATE_TIME, LONG_CTOR, .ctor, DEFINE_CLASS(DECIMAL, System, Decimal) DEFINE_METHOD(DECIMAL, CURRENCY_CTOR, .ctor, IM_Currency_RetVoid) -DEFINE_CLASS_U(System, Delegate, NoClass) -DEFINE_FIELD_U(_helperObject, DelegateObject, _helperObject) -DEFINE_FIELD_U(_firstParameter, DelegateObject, _firstParameter) -DEFINE_FIELD_U(_functionPointer, DelegateObject, _functionPointer) -DEFINE_FIELD_U(_extraFunctionPointerOrData, DelegateObject, _extraFunctionPointerOrData) -DEFINE_FIELD_U(_invocationCount, DelegateObject, _invocationCount) -DEFINE_FIELD_U(_methodDesc, DelegateObject, _methodDesc) -DEFINE_CLASS(DELEGATE, System, Delegate) -DEFINE_FIELD(DELEGATE, HELPER_OBJECT, _helperObject) -DEFINE_FIELD(DELEGATE, FIRST_PARAMETER, _firstParameter) -DEFINE_FIELD(DELEGATE, FUNCTION_POINTER, _functionPointer) -DEFINE_FIELD(DELEGATE, EXTRA_FUNCTION_POINTER_OR_DATA, _extraFunctionPointerOrData) -DEFINE_FIELD(DELEGATE, INVOCATION_COUNT, _invocationCount) -DEFINE_METHOD(DELEGATE, CONSTRUCT_DELEGATE, DelegateConstruct, IM_Obj_IntPtr_RetVoid) -DEFINE_METHOD(DELEGATE, GET_INVOKE_METHOD, GetInvokeMethod, IM_RetIntPtr) +DEFINE_CLASS_U(System, Delegate, NoClass) +DEFINE_FIELD_U(_invocationList, DelegateObject, _invocationList) +DEFINE_FIELD_U(_target, DelegateObject, _target) +DEFINE_FIELD_U(_methodPtr, DelegateObject, _methodPtr) +DEFINE_FIELD_U(_methodPtrAux, DelegateObject, _methodPtrAux) +DEFINE_FIELD_U(_invocationCount, DelegateObject, _invocationCount) +DEFINE_FIELD_U(_methodDesc, DelegateObject, _methodDesc) +DEFINE_CLASS(DELEGATE, System, Delegate) +DEFINE_FIELD(DELEGATE, INVOCATION_LIST, _invocationList) +DEFINE_FIELD(DELEGATE, TARGET, _target) +DEFINE_FIELD(DELEGATE, METHOD_PTR, _methodPtr) +DEFINE_FIELD(DELEGATE, METHOD_PTR_AUX, _methodPtrAux) +DEFINE_FIELD(DELEGATE, INVOCATION_COUNT, _invocationCount) +DEFINE_METHOD(DELEGATE, CONSTRUCT_DELEGATE, DelegateConstruct, IM_Obj_IntPtr_RetVoid) +DEFINE_METHOD(DELEGATE, GET_INVOKE_METHOD, GetInvokeMethod, IM_RetIntPtr) DEFINE_CLASS(INT128, System, Int128) DEFINE_CLASS(UINT128, System, UInt128) diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index 7315c171a2d277..830cc01e966fa1 100644 --- a/src/coreclr/vm/dllimport.cpp +++ b/src/coreclr/vm/dllimport.cpp @@ -2207,7 +2207,7 @@ void PInvokeStubLinker::Begin(DWORD dwStubFlags) m_pcsDispatch->EmitADD(); m_pcsDispatch->EmitLDIND_I(); // get OBJECTHANDLE m_pcsDispatch->EmitLDIND_REF(); // get Delegate object - m_pcsDispatch->EmitLDFLD(GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__HELPER_OBJECT))); + m_pcsDispatch->EmitLDFLD(GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__TARGET))); #endif // FEATURE_PORTABLE_ENTRYPOINTS } } @@ -2407,7 +2407,7 @@ void PInvokeStubLinker::DoPInvoke(ILCodeStream *pcsEmit, DWORD dwStubFlags, Meth #else // !FEATURE_PORTABLE_ENTRYPOINTS if (SF_IsDelegateStub(dwStubFlags)) // reverse P/Invoke via delegate { - int tokDelegate_methodPtr = pcsEmit->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__FUNCTION_POINTER)); + int tokDelegate_methodPtr = pcsEmit->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__METHOD_PTR)); EmitLoadStubContext(pcsEmit, dwStubFlags); pcsEmit->EmitLDC(offsetof(UMEntryThunkData, m_pObjectHandle)); diff --git a/src/coreclr/vm/dllimportcallback.h b/src/coreclr/vm/dllimportcallback.h index 83e6dc8d11e730..da140e3f8aedcf 100644 --- a/src/coreclr/vm/dllimportcallback.h +++ b/src/coreclr/vm/dllimportcallback.h @@ -308,7 +308,7 @@ class UMEntryThunkData // We have optimizations that skip the Invoke method and call directly the // delegate's target method. We need to return the target in that case, // otherwise debugger would fail to step in. - RETURN orDelegate->GetFunctionPointer(); + RETURN orDelegate->GetMethodPtr(); } else if (m_pManagedTarget != (PCODE)NULL) { diff --git a/src/coreclr/vm/i386/stublinkerx86.cpp b/src/coreclr/vm/i386/stublinkerx86.cpp index 57fdbc0452a3a3..3c37645e2cd66b 100644 --- a/src/coreclr/vm/i386/stublinkerx86.cpp +++ b/src/coreclr/vm/i386/stublinkerx86.cpp @@ -1194,9 +1194,9 @@ VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) } // mov r10, [r11 + Delegate._methodptraux] - X86EmitIndexRegLoad(kR10, kR11, DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); - // add r11, DelegateObject::GetOffsetOfExtraFunctionPointerOrData() - load the indirection cell into r11 - X86EmitAddReg(kR11, DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); + X86EmitIndexRegLoad(kR10, kR11, DelegateObject::GetOffsetOfMethodPtrAux()); + // add r11, DelegateObject::GetOffsetOfMethodPtrAux() - load the indirection cell into r11 + X86EmitAddReg(kR11, DelegateObject::GetOffsetOfMethodPtrAux()); // Now jump to real target // jmp r10 static const BYTE bjmpr10[] = { 0x41, 0xff, 0xe2 }; @@ -1287,7 +1287,7 @@ VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) // Now jump to real target // JMP [SCRATCHREG] // we need to jump indirect so that for virtual delegates eax contains a pointer to the indirection cell - X86EmitAddReg(SCRATCH_REGISTER_X86REG, DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); + X86EmitAddReg(SCRATCH_REGISTER_X86REG, DelegateObject::GetOffsetOfMethodPtrAux()); static const BYTE bjmpeax[] = { 0xff, 0x20 }; EmitBytes(bjmpeax, sizeof(bjmpeax)); diff --git a/src/coreclr/vm/i386/virtualcallstubcpu.hpp b/src/coreclr/vm/i386/virtualcallstubcpu.hpp index 1e133f745a06ad..f85c5f557d5ec1 100644 --- a/src/coreclr/vm/i386/virtualcallstubcpu.hpp +++ b/src/coreclr/vm/i386/virtualcallstubcpu.hpp @@ -457,7 +457,7 @@ BOOL isDelegateCall(BYTE *interiorPtr) if (GCHeapUtilities::GetGCHeap()->IsHeapPointer((void*)interiorPtr)) { - Object *delegate = (Object*)(interiorPtr - DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); + Object *delegate = (Object*)(interiorPtr - DelegateObject::GetOffsetOfMethodPtrAux()); VALIDATEOBJECTREF(ObjectToOBJECTREF(delegate)); _ASSERTE(delegate->GetMethodTable()->IsDelegate()); diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 4aa986869b3aba..7ef6d91d3506ed 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -3305,7 +3305,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr DELEGATEREF* delegateObj = LOCAL_VAR_ADDR(callArgsOffset, DELEGATEREF); NULL_CHECK(*delegateObj); - PCODE targetAddress = (*delegateObj)->GetFunctionPointer(); + PCODE targetAddress = (*delegateObj)->GetMethodPtr(); DelegateEEClass *pDelClass = (DelegateEEClass*)(*delegateObj)->GetMethodTable()->GetClass(); if ((pDelClass->m_pInstRetBuffCallStub != NULL && pDelClass->m_pInstRetBuffCallStub->GetEntryPoint() == targetAddress) || (pDelClass->m_pStaticCallStub != NULL && pDelClass->m_pStaticCallStub->GetEntryPoint() == targetAddress)) @@ -3313,7 +3313,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr // This implies that we're using a delegate shuffle thunk to strip off the first parameter to the method // and call the actual underlying method. We allow for tail-calls to work and for greater efficiency in the // interpreter by skipping the shuffle thunk and calling the actual target method directly. - PCODE actualTarget = (*delegateObj)->GetExtraFunctionPointerOrData(); + PCODE actualTarget = (*delegateObj)->GetMethodPtrAux(); // Detect open virtual dispatch scenario bool isOpenVirtual = false; @@ -3390,7 +3390,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr } } - OBJECTREF targetMethodObj = (*delegateObj)->GetFirstParameter(); + OBJECTREF targetMethodObj = (*delegateObj)->GetTarget(); LOCAL_VAR(callArgsOffset, OBJECTREF) = targetMethodObj; if ((targetMethod = NonVirtualEntry2MethodDesc(targetAddress)) != NULL) diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 34395ee0ed6f7d..a71a92fc5519e4 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1903,9 +1903,9 @@ HCIMPL2(void, JIT_DelegateProfile32, Object *obj, ICorJitInfo::HandleHistogram32 // MethodDesc* pRecordedMD = (MethodDesc*)DEFAULT_UNKNOWN_HANDLE; DELEGATEREF del = (DELEGATEREF)objRef; - if ((del->GetInvocationCount() == 0) && (del->GetExtraFunctionPointerOrData() == (PCODE)NULL)) + if ((del->GetInvocationCount() == 0) && (del->GetMethodPtrAux() == (PCODE)NULL)) { - MethodDesc* pMD = NonVirtualEntry2MethodDesc(del->GetFunctionPointer()); + MethodDesc* pMD = NonVirtualEntry2MethodDesc(del->GetMethodPtr()); if ((pMD != nullptr) && !pMD->GetLoaderAllocator()->IsCollectible() && !pMD->IsDynamicMethod()) { pRecordedMD = pMD; @@ -1949,9 +1949,9 @@ HCIMPL2(void, JIT_DelegateProfile64, Object *obj, ICorJitInfo::HandleHistogram64 // MethodDesc* pRecordedMD = (MethodDesc*)DEFAULT_UNKNOWN_HANDLE; DELEGATEREF del = (DELEGATEREF)objRef; - if ((del->GetInvocationCount() == 0) && (del->GetExtraFunctionPointerOrData() == (PCODE)NULL)) + if ((del->GetInvocationCount() == 0) && (del->GetMethodPtrAux() == (PCODE)NULL)) { - MethodDesc* pMD = NonVirtualEntry2MethodDesc(del->GetFunctionPointer()); + MethodDesc* pMD = NonVirtualEntry2MethodDesc(del->GetMethodPtr()); if ((pMD != nullptr) && !pMD->GetLoaderAllocator()->IsCollectible() && !pMD->IsDynamicMethod()) { pRecordedMD = pMD; diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 153da51e71eed9..c0ec22f3e75a8f 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -10324,18 +10324,18 @@ void CEEInfo::getEEInfo(CORINFO_EE_INFO *pEEInfoOut) #ifndef CROSSBITNESS_COMPILE // The assertions must hold in every non-crossbitness scenario - _ASSERTE(OFFSETOF__DelegateObject__firstParameter == DelegateObject::GetOffsetOfFirstParameter()); - _ASSERTE(OFFSETOF__DelegateObject__functionPointer == DelegateObject::GetOffsetOfFunctionPointer()); - _ASSERTE(OFFSETOF__DelegateObject__extraFunctionPointerOrData == DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); - _ASSERTE(OFFSETOF__PtrArray__m_Array_ == PtrArray::GetDataOffset()); + _ASSERTE(OFFSETOF__DelegateObject__target == DelegateObject::GetOffsetOfTarget()); + _ASSERTE(OFFSETOF__DelegateObject__methodPtr == DelegateObject::GetOffsetOfMethodPtr()); + _ASSERTE(OFFSETOF__DelegateObject__methodPtrAux == DelegateObject::GetOffsetOfMethodPtrAux()); + _ASSERTE(OFFSETOF__PtrArray__m_Array_ == PtrArray::GetDataOffset()); #endif // Delegate offsets - pEEInfoOut->offsetOfDelegateInstance = OFFSETOF__DelegateObject__firstParameter; - pEEInfoOut->offsetOfDelegateFirstTarget = OFFSETOF__DelegateObject__functionPointer; + pEEInfoOut->offsetOfDelegateInstance = OFFSETOF__DelegateObject__target; + pEEInfoOut->offsetOfDelegateFirstTarget = OFFSETOF__DelegateObject__methodPtr; // Wrapper delegate offsets - pEEInfoOut->offsetOfWrapperDelegateIndirectCell = OFFSETOF__DelegateObject__extraFunctionPointerOrData; + pEEInfoOut->offsetOfWrapperDelegateIndirectCell = OFFSETOF__DelegateObject__methodPtrAux; pEEInfoOut->sizeOfReversePInvokeFrame = TARGET_POINTER_SIZE * READYTORUN_ReversePInvokeTransitionFrameSizeInPointerUnits; diff --git a/src/coreclr/vm/loongarch64/stubs.cpp b/src/coreclr/vm/loongarch64/stubs.cpp index 1027f90cc4c339..fc5a820e0aca07 100644 --- a/src/coreclr/vm/loongarch64/stubs.cpp +++ b/src/coreclr/vm/loongarch64/stubs.cpp @@ -811,9 +811,9 @@ VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) // On entry a0 holds the delegate instance. Look up the real target address stored in the MethodPtrAux // field and saved in $r21. Tailcall to the target method after re-arranging the arguments // ld.d $r21, $r4, offsetof(DelegateObject, _methodPtrAux) - EmitLoadStoreRegImm(eLOAD, IntReg(21)/*$r21*/, IntReg(4)/*$a0*/, DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); - // addi.d t8, a0, DelegateObject::GetOffsetOfExtraFunctionPointerOrData() - load the indirection cell into t8 used by ResolveWorkerAsmStub - EmitAddImm(20/*$t8*/, 4, DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); + EmitLoadStoreRegImm(eLOAD, IntReg(21)/*$r21*/, IntReg(4)/*$a0*/, DelegateObject::GetOffsetOfMethodPtrAux()); + // addi.d t8, a0, DelegateObject::GetOffsetOfMethodPtrAux() - load the indirection cell into t8 used by ResolveWorkerAsmStub + EmitAddImm(20/*$t8*/, 4, DelegateObject::GetOffsetOfMethodPtrAux()); const ShuffleEntry* entry = pShuffleEntryArray; // Shuffle integer argument registers diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index aa01464ead5cd2..98522b670b12e6 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -1752,23 +1752,23 @@ class DelegateObject : public Object friend struct ::cdac_data; public: - BOOL IsWrapperDelegate() { LIMITED_METHOD_CONTRACT; return _extraFunctionPointerOrData == 0; } + BOOL IsWrapperDelegate() { LIMITED_METHOD_CONTRACT; return _methodPtrAux == 0; } - OBJECTREF GetHelperObject() { LIMITED_METHOD_CONTRACT; return _helperObject; } - void SetHelperObject(OBJECTREF helperObject) { WRAPPER_NO_CONTRACT; SetObjectReference(&_helperObject, helperObject); } - static int GetOffsetOfHelperObject() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _helperObject); } + OBJECTREF GetInvocationList() { LIMITED_METHOD_CONTRACT; return _invocationList; } + void SetInvocationList(OBJECTREF invocationList) { WRAPPER_NO_CONTRACT; SetObjectReference(&_invocationList, invocationList); } + static int GetOffsetOfInvocationList() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _invocationList); } - OBJECTREF GetFirstParameter() { LIMITED_METHOD_CONTRACT; return _firstParameter; } - void SetFirstParameter(OBJECTREF firstParameter) { WRAPPER_NO_CONTRACT; SetObjectReference(&_firstParameter, firstParameter); } - static int GetOffsetOfFirstParameter() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _firstParameter); } + OBJECTREF GetTarget() { LIMITED_METHOD_CONTRACT; return _target; } + void SetTarget(OBJECTREF target) { WRAPPER_NO_CONTRACT; SetObjectReference(&_target, target); } + static int GetOffsetOfTarget() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _target); } - PCODE GetFunctionPointer() { LIMITED_METHOD_CONTRACT; return _functionPointer; } - void SetFunctionPointer(PCODE functionPointer) { LIMITED_METHOD_CONTRACT; _functionPointer = functionPointer; } - static int GetOffsetOfFunctionPointer() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _functionPointer); } + PCODE GetMethodPtr() { LIMITED_METHOD_CONTRACT; return _methodPtr; } + void SetMethodPtr(PCODE methodPtr) { LIMITED_METHOD_CONTRACT; _methodPtr = methodPtr; } + static int GetOffsetOfMethodPtr() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtr); } - PCODE GetExtraFunctionPointerOrData() { LIMITED_METHOD_CONTRACT; return _extraFunctionPointerOrData; } - void SetExtraFunctionPointerOrData(PCODE extraFunctionPointerOrData) { LIMITED_METHOD_CONTRACT; _extraFunctionPointerOrData = extraFunctionPointerOrData; } - static int GetOffsetOfExtraFunctionPointerOrData() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _extraFunctionPointerOrData); } + PCODE GetMethodPtrAux() { LIMITED_METHOD_CONTRACT; return _methodPtrAux; } + void SetMethodPtrAux(PCODE methodPtrAux) { LIMITED_METHOD_CONTRACT; _methodPtrAux = methodPtrAux; } + static int GetOffsetOfMethodPtrAux() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtrAux); } INT_PTR GetInvocationCount() { LIMITED_METHOD_CONTRACT; return _invocationCount; } void SetInvocationCount(INT_PTR invocationCount) { LIMITED_METHOD_CONTRACT; _invocationCount = invocationCount; } @@ -1782,26 +1782,25 @@ class DelegateObject : public Object // If you modify the order of these fields, make sure to update the definition in // BCL for this object. private: - OBJECTREF _helperObject; - OBJECTREF _firstParameter; - PCODE _functionPointer; - PCODE _extraFunctionPointerOrData; + OBJECTREF _invocationList; + OBJECTREF _target; + PCODE _methodPtr; + PCODE _methodPtrAux; INT_PTR _invocationCount; MethodDesc* _methodDesc; }; -#define OFFSETOF__DelegateObject__firstParameter (OBJECT_SIZE /* m_pMethTab */ + TARGET_POINTER_SIZE /* _helperObject */) -#define OFFSETOF__DelegateObject__functionPointer (OFFSETOF__DelegateObject__firstParameter + TARGET_POINTER_SIZE /* _firstParameter */) -#define OFFSETOF__DelegateObject__extraFunctionPointerOrData (OFFSETOF__DelegateObject__functionPointer + TARGET_POINTER_SIZE /* _functionPointer */) -#define OFFSETOF__DelegateObject__methodDesc (OFFSETOF__DelegateObject__extraFunctionPointerOrData + TARGET_POINTER_SIZE /* _extraFunctionPointerOrData */ + \ - TARGET_POINTER_SIZE /* _invocationCount */) +#define OFFSETOF__DelegateObject__target (OBJECT_SIZE /* m_pMethTab */ + TARGET_POINTER_SIZE /* _invocationList */) +#define OFFSETOF__DelegateObject__methodPtr (OFFSETOF__DelegateObject__target + TARGET_POINTER_SIZE /* _target */) +#define OFFSETOF__DelegateObject__methodPtrAux (OFFSETOF__DelegateObject__methodPtr + TARGET_POINTER_SIZE /* _methodPtr */) +#define OFFSETOF__DelegateObject__methodDesc (OFFSETOF__DelegateObject__methodPtrAux + TARGET_POINTER_SIZE /* _methodPtrAux */ + TARGET_POINTER_SIZE /* _invocationCount */) template<> struct cdac_data { - static constexpr size_t Target = offsetof(DelegateObject, _firstParameter); - static constexpr size_t MethodPtr = offsetof(DelegateObject, _functionPointer); - static constexpr size_t MethodPtrAux = offsetof(DelegateObject, _extraFunctionPointerOrData); + static constexpr size_t Target = offsetof(DelegateObject, _target); + static constexpr size_t MethodPtr = offsetof(DelegateObject, _methodPtr); + static constexpr size_t MethodPtrAux = offsetof(DelegateObject, _methodPtrAux); static constexpr size_t InvocationCount = offsetof(DelegateObject, _invocationCount); }; diff --git a/src/coreclr/vm/riscv64/stubs.cpp b/src/coreclr/vm/riscv64/stubs.cpp index 9235b6b7e46cff..9064d3f844f5fd 100644 --- a/src/coreclr/vm/riscv64/stubs.cpp +++ b/src/coreclr/vm/riscv64/stubs.cpp @@ -887,9 +887,9 @@ VOID StubLinkerCPU::EmitShuffleThunk(ShuffleEntry *pShuffleEntryArray) static const IntReg t6 = 31, t5 = 30, a0 = argRegBase + 0; // On entry a0 holds the delegate instance. Look up the real target address stored in the MethodPtrAux // field and saved in t6. Tailcall to the target method after re-arranging the arguments - EmitLoad(t6, a0, DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); + EmitLoad(t6, a0, DelegateObject::GetOffsetOfMethodPtrAux()); // load the indirection cell into t5 used by ResolveWorkerAsmStub - EmitAddImm(t5, a0, DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); + EmitAddImm(t5, a0, DelegateObject::GetOffsetOfMethodPtrAux()); const ShuffleEntry* entry = pShuffleEntryArray; // Shuffle integer argument registers diff --git a/src/coreclr/vm/stubhelpers.cpp b/src/coreclr/vm/stubhelpers.cpp index 298885004e5cd5..7e64050dfac66a 100644 --- a/src/coreclr/vm/stubhelpers.cpp +++ b/src/coreclr/vm/stubhelpers.cpp @@ -511,14 +511,14 @@ FCIMPL1(void*, StubHelpers::GetDelegateTarget, DelegateObject *pThisUNSAFE) DELEGATEREF orefThis = (DELEGATEREF)ObjectToOBJECTREF(pThisUNSAFE); #if defined(HOST_64BIT) - UINT_PTR target = (UINT_PTR)orefThis->GetExtraFunctionPointerOrData(); + UINT_PTR target = (UINT_PTR)orefThis->GetMethodPtrAux(); // See code:GenericPInvokeCalliHelper // The lowest bit is used to distinguish between MD and target on 64-bit. target = (target << 1) | 1; #endif // HOST_64BIT - pEntryPoint = orefThis->GetExtraFunctionPointerOrData(); + pEntryPoint = orefThis->GetMethodPtrAux(); return (PVOID)pEntryPoint; } diff --git a/src/coreclr/vm/stubmgr.cpp b/src/coreclr/vm/stubmgr.cpp index 6bd5424250a18d..e8f55803ece74a 100644 --- a/src/coreclr/vm/stubmgr.cpp +++ b/src/coreclr/vm/stubmgr.cpp @@ -1208,7 +1208,7 @@ BOOL StubLinkStubManager::TraceDelegateObject(BYTE* pbDel, TraceDestination *tra // If we got here, then we're here b/c we're at the start of a delegate stub // need to figure out the kind of delegates we are dealing with. - BYTE *pbDelInvocationList = *(BYTE **)(pbDel + DelegateObject::GetOffsetOfHelperObject()); + BYTE *pbDelInvocationList = *(BYTE **)(pbDel + DelegateObject::GetOffsetOfInvocationList()); LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: invocationList: %p\n", pbDelInvocationList)); @@ -1220,10 +1220,10 @@ BOOL StubLinkStubManager::TraceDelegateObject(BYTE* pbDel, TraceDestination *tra // For the others the logic is the following: // if _methodPtrAux is 0 the target is in _methodPtr, otherwise the taret is _methodPtrAux - ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); + ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtrAux()); if (*ppbDest == NULL) { - ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfFunctionPointer()); + ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtr()); if (*ppbDest == NULL) { @@ -1250,7 +1250,7 @@ BOOL StubLinkStubManager::TraceDelegateObject(BYTE* pbDel, TraceDestination *tra if (pbCount == NULL) { // it's a static closed, the target lives in _methodAuxPtr - ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfExtraFunctionPointerOrData()); + ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtrAux()); if (*ppbDest == NULL) { @@ -1374,7 +1374,7 @@ static BOOL TraceShuffleThunk( *pRetAddr = (BYTE *)StubManagerHelpers::GetReturnAddress(pContext); DELEGATEREF orDelegate = (DELEGATEREF)ObjectToOBJECTREF(StubManagerHelpers::GetThisPtr(pContext)); - PCODE destAddr = orDelegate->GetExtraFunctionPointerOrData(); + PCODE destAddr = orDelegate->GetMethodPtrAux(); LOG((LF_CORDB,LL_INFO10000, "TraceShuffleThunk: ppbDest: %p\n", destAddr)); BOOL res = StubManager::TraceStub(destAddr, trace); @@ -1763,7 +1763,7 @@ BOOL ILStubManager::TraceManager(Thread *thread, { // This is forward delegate P/Invoke stub, the argument is undefined DelegateObject *pDel = (DelegateObject *)pThis; - target = pDel->GetExtraFunctionPointerOrData(); + target = pDel->GetMethodPtrAux(); LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: Forward delegate P/Invoke case %p\n", target)); trace->InitForUnmanaged(target); diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index de07be5c0a4a34..dd256a50499126 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -1446,7 +1446,7 @@ extern "C" PCODE CID_VirtualOpenDelegateDispatchWorker(TransitionBlock * pTransi MODE_COOPERATIVE; } CONTRACTL_END; - OBJECTREF delegateObj = ObjectToOBJECTREF((Object*)(((BYTE*)ppMethodPtrAuxField) - DelegateObject::GetOffsetOfExtraFunctionPointerOrData())); + OBJECTREF delegateObj = ObjectToOBJECTREF((Object*)(((BYTE*)ppMethodPtrAuxField) - DelegateObject::GetOffsetOfMethodPtrAux())); MAKE_CURRENT_THREAD_AVAILABLE(); #ifdef _DEBUG From 8a91b95bff83281fddf6c96e6627bc1f2170181b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Wed, 17 Jun 2026 04:29:10 +0200 Subject: [PATCH 32/77] Fix comments --- src/coreclr/vm/comdelegate.cpp | 38 +++++++++++++++++----------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 6676d6fe03a9e7..c5e45ab89f06c4 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1847,11 +1847,11 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) if (count != 0) { // this is one of the following: - // - multicast - _helperObject is Array && _invocationCount != 0 - // - unamanaged ftn ptr - _helperObject == NULL && _invocationCount == -1 - // - wrapper delegate - _helperObject is Delegate && _invocationCount != NULL - // - virtual delegate - _helperObject == null && _invocationCount == (target MethodDesc) - // or _helperObject points to a LoaderAllocator/DynamicResolver (inner open virtual delegate of a Wrapper Delegate) + // - multicast - _invocationList is Array && _invocationCount != 0 + // - unamanaged ftn ptr - _invocationList == NULL && _invocationCount == -1 + // - wrapper delegate - _invocationList is Delegate && _invocationCount != NULL + // - virtual delegate - _invocationList == null && _invocationCount == (target MethodDesc) + // or _invocationList points to a LoaderAllocator/DynamicResolver (inner open virtual delegate of a Wrapper Delegate) // in the wrapper delegate case we want to unwrap and return the method desc of the inner delegate // in the other cases we return the method desc for the invoke bool fOpenVirtualDelegate = false; @@ -1922,8 +1922,8 @@ OBJECTREF COMDelegate::GetTargetObject(OBJECTREF obj) // - multicast // - unmanaged ftn ptr // - wrapper delegate - // - virtual delegate - _helperObject == null && _invocationCount == (target MethodDesc) - // or _helperObject points to a LoaderAllocator/DynamicResolver (inner open virtual delegate of a Wrapper Delegate) + // - virtual delegate - _invocationList == null && _invocationCount == (target MethodDesc) + // or _invocationList points to a LoaderAllocator/DynamicResolver (inner open virtual delegate of a Wrapper Delegate) // in the wrapper delegate case we want to unwrap and return the object of the inner delegate OBJECTREF innerDel = thisDel->GetInvocationList(); if (innerDel != NULL) @@ -2081,7 +2081,7 @@ BOOL COMDelegate::NeedsWrapperDelegate(MethodDesc* pTargetMD) // to create a wrapper delegate wrapper we need: -// - the delegate to forward to -> _helperObject +// - the delegate to forward to -> _invocationList // - the delegate invoke MethodDesc -> _count // the 2 fields used for invocation will contain: // - the delegate itself -> _pORField @@ -2794,7 +2794,7 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT BOOL isCollectible = pTargetMethodLoaderAllocator->IsCollectible(); // A method that may be instantiated over a collectible type, and is static will require a delegate // that has the LoaderAllocator of the collectible assembly associated with the instantiation - // stored in the _helperObject field. + // stored in the _invocationList field. BOOL fMaybeCollectibleAndStatic = FALSE; if (isStatic) @@ -2858,27 +2858,27 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT // DELEGATE KINDS TABLE // - // _helperObject _firstParameter _functionPointer _extraFunctionPointerOrData _invocationCount + // _invocationList _target _methodPtr _methodPtrAux _invocationCount // - // 1- Instance closed null 'this' ptr target method null 0 - // 2- Instance open non-virt null delegate shuffle thunk target method 0 - // 3- Instance open virtual null delegate Virtual-stub dispatch method id 0 - // 4- Static closed null first arg target method null 0 - // 5- Static closed (special sig) null first arg ThisPtrRetBuf precode null 0 - // 6- Static opened null delegate shuffle thunk target method 0 - // 7- Wrapper target delegate other delegate call thunk MethodDesc (frame) (arm only, VSD indirection cell address) + // 1- Instance closed null 'this' ptr target method null 0 + // 2- Instance open non-virt null delegate shuffle thunk target method 0 + // 3- Instance open virtual null delegate Virtual-stub dispatch method id 0 + // 4- Static closed null first arg target method null 0 + // 5- Static closed (special sig) null first arg ThisPtrRetBuf precode null 0 + // 6- Static opened null delegate shuffle thunk target method 0 + // 7- Wrapper target delegate other delegate call thunk MethodDesc (frame) (arm only, VSD indirection cell address) // // Delegate invoke arg count == target method arg count - 2, 3, 6 // Delegate invoke arg count == 1 + target method arg count - 1, 4, 5 // - // 1, 4 - MulticastDelegate.ctor1 (simply assign _firstParameter and _functionPointer) + // 1, 4 - MulticastDelegate.ctor1 (simply assign _target and _methodPtr) // 5 - MulticastDelegate.ctor2 (see table, takes 3 args) // 2, 6 - MulticastDelegate.ctor3 (take shuffle thunk) // 3 - MulticastDelegate.ctor4 (take shuffle thunk, retrieve MethodDesc) ??? // // 7 - Needs special handling // - // With collectible types, we need to fill the _helperObject field in with a value that represents the LoaderAllocator of the target method + // With collectible types, we need to fill the _invocationList field in with a value that represents the LoaderAllocator of the target method // if the delegate is not a closed instance delegate. // // There are two techniques that will work for this. From 94a9b25382298f0603b21090eefd087040085341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Wed, 17 Jun 2026 04:33:09 +0200 Subject: [PATCH 33/77] Revert missed field --- .../src/System/Delegate.CoreCLR.cs | 20 +++++++++---------- .../src/System/MulticastDelegate.CoreCLR.cs | 20 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 98b060926b1270..9860dfabc0a848 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -24,9 +24,9 @@ public abstract partial class Delegate : ICloneable, ISerializable // 4. Collectible delegates internal object? _invocationList; // Initialized by VM as needed - // _firstParameter is the object we will invoke on; null if static delegate - // Keep _firstParameter and _methodPtr next to each other for optimal delegate invoke performance - internal object? _firstParameter; + // _target is the object we will invoke on; null if static delegate + // Keep _target and _methodPtr next to each other for optimal delegate invoke performance + internal object? _target; // _methodPtr is a pointer to the method we will invoke // It could be a small thunk if this is a static or UM call @@ -146,7 +146,7 @@ public override bool Equals([NotNullWhen(true)] object? obj) Delegate other = Unsafe.AsAssert(obj); // do an optimistic check first. This is hopefully cheap enough to be worth - if (_firstParameter == other._firstParameter && + if (_target == other._target && _methodPtr == other._methodPtr && _methodPtrAux == other._methodPtrAux) return true; @@ -163,7 +163,7 @@ public override bool Equals([NotNullWhen(true)] object? obj) return false; // different delegate kind // they are both closed over the first arg - if (_firstParameter != other._firstParameter) + if (_target != other._target) return false; // fall through method handle check @@ -187,16 +187,16 @@ public override bool Equals([NotNullWhen(true)] object? obj) public override int GetHashCode() { int hashCode = MethodDesc.GetHashCode(); - if (_methodPtrAux == 0 && _firstParameter != null) + if (_methodPtrAux == 0 && _target != null) { - hashCode += RuntimeHelpers.GetHashCode(_firstParameter) * 33; + hashCode += RuntimeHelpers.GetHashCode(_target) * 33; } return hashCode; } internal virtual object? GetTarget() { - return _methodPtrAux == 0 ? _firstParameter : null; + return _methodPtrAux == 0 ? _target : null; } protected virtual MethodInfo GetMethodImpl() @@ -263,7 +263,7 @@ private MethodInfo GetMethodImplCore() // walking won't be we compare using the generic type definition forms instead. Type targetType = declaringType.GetGenericTypeDefinition(); Type? currentType; - for (currentType = _firstParameter!.GetType(); currentType != null; currentType = currentType.BaseType) + for (currentType = _target!.GetType(); currentType != null; currentType = currentType.BaseType) { if (currentType.IsGenericType && currentType.GetGenericTypeDefinition() == targetType) @@ -279,7 +279,7 @@ private MethodInfo GetMethodImplCore() // The targetType may also be an interface with a Default interface method (DIM). Debug.Assert( currentType != null - || _firstParameter.GetType().IsCOMObject + || _target.GetType().IsCOMObject || targetType.IsInterface, "The class hierarchy should declare the method or be a DIM"); } else diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index a3e087f528ce17..e33f14bd988a37 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -95,7 +95,7 @@ private static bool TrySetSlot(object?[] a, int index, object o) MulticastDelegate dd = (MulticastDelegate)previous; return dd._methodPtr == d._methodPtr && dd._methodPtrAux == d._methodPtrAux && - dd._firstParameter == d._firstParameter; + dd._target == d._target; } private unsafe MulticastDelegate NewMulticastDelegate(object[] invocationList, int invocationCount, bool thisIsMultiCastAlready) @@ -115,7 +115,7 @@ private unsafe MulticastDelegate NewMulticastDelegate(object[] invocationList, i result._methodPtr = GetMulticastInvoke(); result._methodPtrAux = GetInvokeMethod(); } - result._firstParameter = result; + result._target = result; result._invocationList = invocationList; result._invocationCount = invocationCount; @@ -424,7 +424,7 @@ private void CtorClosed(object target, IntPtr methodPtr) { if (target == null) ThrowNullThisInDelegateToInstance(); - _firstParameter = target; + _target = target; _methodPtr = (nuint)methodPtr; } @@ -432,7 +432,7 @@ private void CtorClosed(object target, IntPtr methodPtr) [DebuggerStepThrough] private void CtorClosedStatic(object target, IntPtr methodPtr) { - _firstParameter = target; + _target = target; _methodPtr = (nuint)methodPtr; } @@ -442,7 +442,7 @@ private void CtorRTClosed(object target, IntPtr methodPtr) { if (target == null) ThrowNullThisInDelegateToInstance(); - _firstParameter = target; + _target = target; _methodPtr = (nuint)AdjustTarget(target, methodPtr); } @@ -450,7 +450,7 @@ private void CtorRTClosed(object target, IntPtr methodPtr) [DebuggerStepThrough] private void CtorOpened(object target, IntPtr methodPtr, IntPtr shuffleThunk) { - _firstParameter = this; + _target = this; _methodPtr = (nuint)shuffleThunk; _methodPtrAux = methodPtr; } @@ -459,7 +459,7 @@ private void CtorOpened(object target, IntPtr methodPtr, IntPtr shuffleThunk) [DebuggerStepThrough] private void CtorVirtualDispatch(object target, IntPtr methodPtr, IntPtr shuffleThunk) { - _firstParameter = this; + _target = this; _methodPtr = (nuint)shuffleThunk; InitializeVirtualCallStub(methodPtr); } @@ -468,7 +468,7 @@ private void CtorVirtualDispatch(object target, IntPtr methodPtr, IntPtr shuffle [DebuggerStepThrough] private void CtorCollectibleClosedStatic(object target, IntPtr methodPtr, IntPtr gchandle) { - _firstParameter = target; + _target = target; _methodPtr = (nuint)methodPtr; _invocationList = GCHandle.InternalGet(gchandle); Debug.Assert(InvocationListLogicallyNull); @@ -478,7 +478,7 @@ private void CtorCollectibleClosedStatic(object target, IntPtr methodPtr, IntPtr [DebuggerStepThrough] private void CtorCollectibleOpened(object target, IntPtr methodPtr, IntPtr shuffleThunk, IntPtr gchandle) { - _firstParameter = this; + _target = this; _methodPtr = (nuint)shuffleThunk; _methodPtrAux = methodPtr; _invocationList = GCHandle.InternalGet(gchandle); @@ -489,7 +489,7 @@ private void CtorCollectibleOpened(object target, IntPtr methodPtr, IntPtr shuff [DebuggerStepThrough] private void CtorCollectibleVirtualDispatch(object target, IntPtr methodPtr, IntPtr shuffleThunk, IntPtr gchandle) { - _firstParameter = this; + _target = this; _methodPtr = (nuint)shuffleThunk; _invocationList = GCHandle.InternalGet(gchandle); Debug.Assert(InvocationListLogicallyNull); From f33e75112b5efc14b001cfcc65d819c4748cf29e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Wed, 17 Jun 2026 04:43:13 +0200 Subject: [PATCH 34/77] Optimize invocations --- .../src/System/MulticastDelegate.CoreCLR.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index e33f14bd988a37..809f7cc072f992 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -329,12 +329,9 @@ public sealed override Delegate[] GetInvocationList() return [this]; } - // Create an array of delegate copies and copy each element into the array - ReadOnlySpan invocationList = GetInvocationsUnchecked(); - Delegate[] del = new Delegate[invocationList.Length]; - for (int i = 0; i < invocationList.Length; i++) - del[i] = invocationList[i]; - return del; + // Copy the invocations into a new array + ReadOnlySpan invocationList = GetInvocationsUnchecked(); + return invocationList.ToArray(); } internal new bool HasSingleTarget => _invocationList is not object[]; From 20bd567d1953b60bd85e9a6d61ac8a711a1cb3e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Wed, 17 Jun 2026 05:23:44 +0200 Subject: [PATCH 35/77] More cleanup --- .../src/System/MulticastDelegate.CoreCLR.cs | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index 809f7cc072f992..c86bf2403815c1 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -54,16 +54,14 @@ public sealed override bool Equals([NotNullWhen(true)] object? obj) case Delegate wrapper: return wrapper.Equals(other); default: - if (IsUnmanagedFunctionPtr) + if (!IsUnmanagedFunctionPtr) { - return other.IsUnmanagedFunctionPtr && - _methodPtr == other._methodPtr && - _methodPtrAux == other._methodPtrAux; + break; } - // now we know 'this' is not a special one, so we can work out what the other is - // this is a wrapper delegate so we need to unwrap and check the inner one - return other._invocationList is Delegate ? Equals(other._invocationList) : base.Equals(obj); + return other.IsUnmanagedFunctionPtr && + _methodPtr == other._methodPtr && + _methodPtrAux == other._methodPtrAux; } } @@ -271,20 +269,20 @@ private static bool EqualInvocationLists(object[] a, object[] b, int start, int continue; } - if (invocationCount - vInvocationCount == 0) - { - // Special case - no values left - return null; - } - - if (invocationCount - vInvocationCount == 1) + switch (invocationCount - vInvocationCount) { - // Special case - only one value left, either at the beginning or the end - return (Delegate)invocationList[i != 0 ? 0 : invocationCount - 1]; + case 0: + // Special case - no values left + return null; + case 1: + // Special case - only one value left, either at the beginning or the end + return (Delegate)invocationList[i != 0 ? 0 : invocationCount - 1]; + default: + { + object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, vInvocationCount); + return NewMulticastDelegate(list, invocationCount - vInvocationCount, true); + } } - - object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, vInvocationCount); - return NewMulticastDelegate(list, invocationCount - vInvocationCount, true); } } } From 15c9c656d1168c176f2e100396987344e088d2a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Wed, 17 Jun 2026 06:08:45 +0200 Subject: [PATCH 36/77] Fix multicast equality --- .../src/System/MulticastDelegate.CoreCLR.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index c86bf2403815c1..0266b8b740ac55 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -49,7 +49,7 @@ public sealed override bool Equals([NotNullWhen(true)] object? obj) switch (_invocationList) { case object[]: - return GetInvocationsUnchecked().SequenceEqual(other.GetInvocationsUnchecked()); + return other._invocationList is object[] && GetInvocationsUnchecked().SequenceEqual(other.GetInvocationsUnchecked()); // this is a wrapper delegate so we need to unwrap and check the inner one case Delegate wrapper: return wrapper.Equals(other); From 68d5505287f79166aefd2f1b850101e86aff367d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Sat, 20 Jun 2026 00:17:55 +0200 Subject: [PATCH 37/77] Address feedback, cleanup --- .../src/System/Delegate.CoreCLR.cs | 98 +++------- .../src/System/MulticastDelegate.CoreCLR.cs | 181 +++++++----------- src/coreclr/vm/comdelegate.cpp | 17 +- .../src/System/Delegate.cs | 16 +- .../System/Runtime/CompilerServices/Unsafe.cs | 9 - .../System.Runtime/ref/System.Runtime.cs | 2 +- .../src/System/Delegate.Mono.cs | 2 +- 7 files changed, 114 insertions(+), 211 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 9860dfabc0a848..f8fe70a5f56b77 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -17,11 +17,10 @@ namespace System [NonVersionable] public abstract partial class Delegate : ICloneable, ISerializable { - // This is set under 4 circumstances + // This is set under 3 circumstances // 1. Multicast delegate - // 2. Wrapper delegate - // 3. Method cache - // 4. Collectible delegates + // 2. Method cache + // 3. Collectible delegates internal object? _invocationList; // Initialized by VM as needed // _target is the object we will invoke on; null if static delegate @@ -52,36 +51,27 @@ private nuint MethodDesc } } - internal bool IsSpecialDelegate => _invocationCount != 0; + internal bool IsMulticastOrUnmanagedOrOpenVirtual => _invocationCount != 0; internal bool IsUnmanagedFunctionPtr => _invocationCount == -1; - internal bool InvocationListLogicallyNull => _invocationList is not (object[] or Delegate); + public partial bool HasSingleTarget => _invocationList is not object[]; - private object[] GetObjectArray() + internal bool TryGetInvocations(out ReadOnlySpan invocations) { - Debug.Assert(IsSpecialDelegate); - - object[]? invocationList = Unsafe.AsAssert(_invocationList); + if (_invocationList is not object[] invocationList) + { + invocations = default; + return false; + } - Debug.Assert(invocationList is not null); Debug.Assert(invocationList.Length > 1); Debug.Assert(invocationList[0] is MulticastDelegate); - Debug.Assert((uint)invocationList.Length >= (nuint)_invocationCount); - return invocationList; - } - internal ReadOnlySpan GetInvocationsUnchecked() - { - ref MulticastDelegate first = ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(GetObjectArray())); - return MemoryMarshal.CreateReadOnlySpan(ref first, (int)_invocationCount); - } - - internal MulticastDelegate GetLastInvocationUnchecked() - { - object last = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(GetObjectArray()), (nuint)_invocationCount - 1); - return Unsafe.AsAssert(last); + ref MulticastDelegate first = ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(invocationList)); + invocations = MemoryMarshal.CreateReadOnlySpan(ref first, (int)_invocationCount); + return true; } // This constructor is called from the class generated by the @@ -143,7 +133,8 @@ public override bool Equals([NotNullWhen(true)] object? obj) if (obj == null || !InternalEqualTypes(this, obj)) return false; - Delegate other = Unsafe.AsAssert(obj); + Debug.Assert(obj is Delegate); + Delegate other = Unsafe.As(obj); // do an optimistic check first. This is hopefully cheap enough to be worth if (_target == other._target && @@ -201,18 +192,14 @@ public override int GetHashCode() protected virtual MethodInfo GetMethodImpl() { - return _invocationList switch - { - MethodInfo methodInfo => methodInfo, - object[] => GetLastInvocationUnchecked().Method, - Delegate wrapper => wrapper.Method, - _ => GetMethodImplUncached() - }; + return TryGetInvocations(out ReadOnlySpan invocations) + ? invocations[^1].Method + : _invocationList as MethodInfo ?? GetMethodImplUncached(); [MethodImpl(MethodImplOptions.NoInlining)] MethodInfo GetMethodImplUncached() { - Debug.Assert(InvocationListLogicallyNull); + Debug.Assert(HasSingleTarget); MethodInfo method = GetMethodImplCore(); Debug.Assert(method is not null); @@ -225,7 +212,7 @@ MethodInfo GetMethodImplUncached() private MethodInfo GetMethodImplCore() { // should be handled by GetMethodImpl - Debug.Assert(InvocationListLogicallyNull); + Debug.Assert(HasSingleTarget); IRuntimeMethodInfo method = CreateMethodInfo(MethodDesc); RuntimeType? declaringType = RuntimeMethodHandle.GetDeclaringType(method); @@ -295,22 +282,11 @@ private MethodInfo GetMethodImplCore() internal void StoreDynamicMethod(MethodInfo dynamicMethod) { - if (IsSpecialDelegate) - { - Debug.Assert(!IsUnmanagedFunctionPtr, "dynamic method and unmanaged fntptr delegate combined"); - - // must be a secure/wrapper one, unwrap and save - Delegate d = ((Delegate?)_invocationList)!; - d.StoreDynamicMethod(dynamicMethod); - return; - } - - Debug.Assert(InvocationListLogicallyNull, "dynamic method with invocation list"); + Debug.Assert(!IsUnmanagedFunctionPtr); + Debug.Assert(HasSingleTarget, "dynamic method with invocation list"); _invocationList = dynamicMethod; } - public object? Target => GetTarget(); - // V1 API. [RequiresUnreferencedCode("The target method might be removed")] public static Delegate? CreateDelegate(Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure) @@ -338,10 +314,7 @@ internal void StoreDynamicMethod(MethodInfo dynamicMethod) DelegateBindingFlags.NeverCloseOverNull | (ignoreCase ? DelegateBindingFlags.CaselessMatching : 0))) { - if (throwOnBindFailure) - throw new ArgumentException(SR.Arg_DlgtTargMeth); - - return null; + return throwOnBindFailure ? throw new ArgumentException(SR.Arg_DlgtTargMeth) : null; } return d; @@ -374,10 +347,7 @@ internal void StoreDynamicMethod(MethodInfo dynamicMethod) DelegateBindingFlags.OpenDelegateOnly | (ignoreCase ? DelegateBindingFlags.CaselessMatching : 0))) { - if (throwOnBindFailure) - throw new ArgumentException(SR.Arg_DlgtTargMeth); - - return null; + return throwOnBindFailure ? throw new ArgumentException(SR.Arg_DlgtTargMeth) : null; } return d; @@ -412,10 +382,7 @@ internal void StoreDynamicMethod(MethodInfo dynamicMethod) null, DelegateBindingFlags.OpenDelegateOnly | DelegateBindingFlags.RelaxedSignature); - if (d == null && throwOnBindFailure) - throw new ArgumentException(SR.Arg_DlgtTargMeth); - - return d; + return d == null && throwOnBindFailure ? throw new ArgumentException(SR.Arg_DlgtTargMeth) : d; } // V2 API. @@ -444,10 +411,7 @@ internal void StoreDynamicMethod(MethodInfo dynamicMethod) firstArgument, DelegateBindingFlags.RelaxedSignature); - if (d == null && throwOnBindFailure) - throw new ArgumentException(SR.Arg_DlgtTargMeth); - - return d; + return d == null && throwOnBindFailure ? throw new ArgumentException(SR.Arg_DlgtTargMeth) : d; } // @@ -470,8 +434,7 @@ internal static Delegate CreateDelegateNoSecurityCheck(Type type, object? target // Initialize the method... Delegate d = InternalAlloc(rtType); - // This is a new internal API added in Whidbey. Currently it's only - // used by the dynamic method code to generate a wrapper delegate. + // This is a new internal API added in Whidbey. // Allow flexible binding options since the target method is // unambiguously provided to us. @@ -487,10 +450,7 @@ internal static Delegate CreateDelegateNoSecurityCheck(Type type, object? target { Delegate d = InternalAlloc(rtType); - if (d.BindToMethodInfo(firstArgument, rtMethod, rtMethod.GetDeclaringTypeInternal(), flags)) - return d; - else - return null; + return d.BindToMethodInfo(firstArgument, rtMethod, rtMethod.GetDeclaringTypeInternal(), flags) ? d : null; } // diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index 672b35e3004a12..ad0d5ec0a740a6 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -37,41 +37,29 @@ public sealed override bool Equals([NotNullWhen(true)] object? obj) // the types are the same, obj should also be a // MulticastDelegate Debug.Assert(obj is MulticastDelegate, "Shouldn't have failed here since we already checked the types are the same!"); - MulticastDelegate other = Unsafe.AsAssert(obj); + MulticastDelegate other = Unsafe.As(obj); - if (IsSpecialDelegate) + if (IsMulticastOrUnmanagedOrOpenVirtual) { // there are 3 kind of delegate kinds that fall into this bucket // 1- Multicast (_invocationList is Object[]) - // 2- Wrapper (_invocationList is Delegate) - // 3- Unmanaged FntPtr (_invocationList == null) - // 4- Open virtual (_invocationCount == MethodDesc of target, _invocationList == null, LoaderAllocator, or DynamicResolver) - switch (_invocationList) + // 2- Unmanaged FntPtr (_invocationList == null) + // 3- Open virtual (_invocationCount == MethodDesc of target, _invocationList == null, LoaderAllocator, or DynamicResolver) + if (TryGetInvocations(out ReadOnlySpan invocations)) { - case object[]: - return other._invocationList is object[] && GetInvocationsUnchecked().SequenceEqual(other.GetInvocationsUnchecked()); - // this is a wrapper delegate so we need to unwrap and check the inner one - case Delegate wrapper: - return wrapper.Equals(other); - default: - if (!IsUnmanagedFunctionPtr) - { - break; - } + return other.TryGetInvocations(out ReadOnlySpan otherInvocations) && invocations.SequenceEqual(otherInvocations); + } - return other.IsUnmanagedFunctionPtr && - _methodPtr == other._methodPtr && - _methodPtrAux == other._methodPtrAux; + if (IsUnmanagedFunctionPtr) + { + return other.IsUnmanagedFunctionPtr && + _methodPtr == other._methodPtr && + _methodPtrAux == other._methodPtrAux; } } - Debug.Assert(InvocationListLogicallyNull); - // now we know 'this' is not a special one, so we can work out what the other is - return other._invocationList is Delegate ? - // this is a wrapper delegate so we need to unwrap and check the inner one - Equals(other._invocationList) : - // we can call on the base - base.Equals(other); + Debug.Assert(HasSingleTarget); + return base.Equals(other); } private static bool TrySetSlot(object?[] a, int index, object o) @@ -99,7 +87,7 @@ private static bool TrySetSlot(object?[] a, int index, object o) private unsafe MulticastDelegate NewMulticastDelegate(object[] invocationList, int invocationCount, bool thisIsMultiCastAlready) { // First, allocate a new multicast delegate just like this one, i.e. same type as the this object - MulticastDelegate result = Unsafe.AsAssert(RuntimeTypeHandle.InternalAllocNoChecks(RuntimeHelpers.GetMethodTable(this))); + MulticastDelegate result = Unsafe.As(RuntimeTypeHandle.InternalAllocNoChecks(RuntimeHelpers.GetMethodTable(this))); // Performance optimization - if this already points to a true multicast delegate, // copy _methodPtr and _methodPtrAux fields rather than calling into the EE to get them @@ -213,7 +201,8 @@ protected sealed override Delegate CombineImpl(Delegate? follow) private object[] DeleteFromInvocationList(object[] invocationList, int invocationCount, int deleteIndex, int deleteCount) { - object[]? thisInvocationList = Unsafe.AsAssert(_invocationList); + Debug.Assert(_invocationList is object[]); + object[]? thisInvocationList = Unsafe.As(_invocationList); Debug.Assert(thisInvocationList is not null); int allocCount = thisInvocationList.Length; @@ -255,65 +244,66 @@ private static bool EqualInvocationLists(object[] a, object[] b, int start, int if (v == null) return this; - if (!v.HasSingleTarget) + if (v.HasSingleTarget) { if (_invocationList is object[] invocationList) { int invocationCount = (int)_invocationCount; - int vInvocationCount = (int)v._invocationCount; - object[] vInvocationList = (object[])v._invocationList!; - for (int i = invocationCount - vInvocationCount; i >= 0; i--) + for (int i = invocationCount; --i >= 0;) { - if (!EqualInvocationLists(invocationList, vInvocationList, i, vInvocationCount)) + if (!value!.Equals(invocationList[i])) { continue; } - switch (invocationCount - vInvocationCount) + if (invocationCount == 2) { - case 0: - // Special case - no values left - return null; - case 1: - // Special case - only one value left, either at the beginning or the end - return (Delegate)invocationList[i != 0 ? 0 : invocationCount - 1]; - default: - { - object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, vInvocationCount); - return NewMulticastDelegate(list, invocationCount - vInvocationCount, true); - } + // Special case - only one value left, either at the beginning or the end + return (Delegate)invocationList[1 - i]; } + + object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, 1); + return NewMulticastDelegate(list, invocationCount - 1, true); } } + else + { + // they are both not real Multicast + if (Equals(value)) + return null; + } } else { if (_invocationList is object[] invocationList) { int invocationCount = (int)_invocationCount; - for (int i = invocationCount; --i >= 0;) + int vInvocationCount = (int)v._invocationCount; + object[] vInvocationList = (object[])v._invocationList!; + for (int i = invocationCount - vInvocationCount; i >= 0; i--) { - if (!value!.Equals(invocationList[i])) + if (!EqualInvocationLists(invocationList, vInvocationList, i, vInvocationCount)) { continue; } - if (invocationCount == 2) + switch (invocationCount - vInvocationCount) { - // Special case - only one value left, either at the beginning or the end - return (Delegate)invocationList[1 - i]; + case 0: + // Special case - no values left + return null; + case 1: + // Special case - only one value left, either at the beginning or the end + return (Delegate)invocationList[i != 0 ? 0 : invocationCount - 1]; + default: + { + object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, + vInvocationCount); + return NewMulticastDelegate(list, invocationCount - vInvocationCount, true); + } } - - object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, 1); - return NewMulticastDelegate(list, invocationCount - 1, true); } } - else - { - // they are both not real Multicast - if (Equals(value)) - return null; - } } return this; @@ -322,31 +312,20 @@ private static bool EqualInvocationLists(object[] a, object[] b, int start, int // This method returns the Invocation list of this multicast delegate. public sealed override Delegate[] GetInvocationList() { - if (_invocationList is not object[]) - { - return [this]; - } - - // Copy the invocations into a new array - ReadOnlySpan invocationList = GetInvocationsUnchecked(); - return invocationList.ToArray(); + return TryGetInvocations(out ReadOnlySpan invocations) ? ((ReadOnlySpan)invocations).ToArray() : [this]; } - internal new bool HasSingleTarget => _invocationList is not object[]; - // Used by delegate invocation list enumerator internal MulticastDelegate? TryGetAt(int index) { - if (HasSingleTarget) + if (TryGetInvocations(out ReadOnlySpan invocations)) { - if (index == 0) - return this; + if ((uint)index < (uint)invocations.Length) + return invocations[index]; } - else + else if (index == 0) { - ReadOnlySpan invocationList = GetInvocationsUnchecked(); - if ((uint)index < (uint)invocationList.Length) - return invocationList[index]; + return this; } return null; @@ -357,50 +336,28 @@ public sealed override int GetHashCode() if (IsUnmanagedFunctionPtr) return HashCode.Combine(_methodPtr, _methodPtr); - if (IsSpecialDelegate) + if (!IsMulticastOrUnmanagedOrOpenVirtual || !TryGetInvocations(out ReadOnlySpan invocations)) { - switch (_invocationList) - { - case object[]: - { - int hash = 0; - foreach (MulticastDelegate multicastDelegate in GetInvocationsUnchecked()) - { - hash = hash * 33 + multicastDelegate.GetHashCode(); - } - - return hash; - } - case Delegate wrapper: - // this is a wrapper delegate so we need to unwrap and check the inner one - return wrapper.GetHashCode(); - } + return base.GetHashCode(); } - return base.GetHashCode(); + int hash = 0; + foreach (MulticastDelegate multicastDelegate in invocations) + { + hash = hash * 33 + multicastDelegate.GetHashCode(); + } + return hash; } internal override object? GetTarget() { - if (IsSpecialDelegate) + if (IsMulticastOrUnmanagedOrOpenVirtual) { - // IsSpecialDelegate we are in one of these cases: + // IsMulticastOrUnmanagedOrOpenVirtual we are in one of these cases: // - Multicast -> return the target of the last delegate in the list // - unmanaged function pointer - return null // - virtual open delegate - return null - if (InvocationListLogicallyNull) - { - // both open virtual and ftn pointer return null for the target - return null; - } - - switch (_invocationList) - { - case object[]: - return GetLastInvocationUnchecked().Target; - case Delegate receiver: - return receiver.Target; - } + return TryGetInvocations(out ReadOnlySpan invocations) ? invocations[^1].Target : null; } return base.GetTarget(); } @@ -465,7 +422,7 @@ private void CtorCollectibleClosedStatic(object target, IntPtr methodPtr, IntPtr _target = target; _methodPtr = (nuint)methodPtr; _invocationList = GCHandle.InternalGet(gchandle); - Debug.Assert(InvocationListLogicallyNull); + Debug.Assert(HasSingleTarget); } [DebuggerNonUserCode] @@ -476,7 +433,7 @@ private void CtorCollectibleOpened(object target, IntPtr methodPtr, IntPtr shuff _methodPtr = (nuint)shuffleThunk; _methodPtrAux = methodPtr; _invocationList = GCHandle.InternalGet(gchandle); - Debug.Assert(InvocationListLogicallyNull); + Debug.Assert(HasSingleTarget); } [DebuggerNonUserCode] @@ -486,7 +443,7 @@ private void CtorCollectibleVirtualDispatch(object target, IntPtr methodPtr, Int _target = this; _methodPtr = (nuint)shuffleThunk; _invocationList = GCHandle.InternalGet(gchandle); - Debug.Assert(InvocationListLogicallyNull); + Debug.Assert(HasSingleTarget); InitializeVirtualCallStub(methodPtr); } #pragma warning restore IDE0060 diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 63782ded180427..94de384a63b5e4 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1292,11 +1292,9 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, LoaderAllocator *pLoaderAllocator = pTargetMethod->GetLoaderAllocator(); - _ASSERTE(refRealDelegate->GetInvocationList() == NULL); + _ASSERTE((*pRefThis)->GetInvocationList() == NULL); if (pLoaderAllocator->IsCollectible()) - refRealDelegate->SetInvocationList(pLoaderAllocator->GetExposedObject()); - - GCPROTECT_END(); + (*pRefThis)->SetInvocationList(pLoaderAllocator->GetExposedObject()); } // Marshals a delegate to a unmanaged callback. @@ -1830,11 +1828,9 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) // this is one of the following: // - multicast - _invocationList is Array && _invocationCount != 0 // - unamanaged ftn ptr - _invocationList == NULL && _invocationCount == -1 - // - wrapper delegate - _invocationList is Delegate && _invocationCount != NULL // - virtual delegate - _invocationList == null && _invocationCount == (target MethodDesc) - // or _invocationList points to a LoaderAllocator/DynamicResolver (inner open virtual delegate of a Wrapper Delegate) - // in the wrapper delegate case we want to unwrap and return the method desc of the inner delegate - // in the other cases we return the method desc for the invoke + // or _invocationList points to a LoaderAllocator/DynamicResolver + // we return the method desc for the invoke bool fOpenVirtualDelegate = false; OBJECTREF innerDel = thisDel->GetInvocationList(); if (innerDel != NULL) @@ -1902,10 +1898,8 @@ OBJECTREF COMDelegate::GetTargetObject(OBJECTREF obj) // this is one of the following: // - multicast // - unmanaged ftn ptr - // - wrapper delegate // - virtual delegate - _invocationList == null && _invocationCount == (target MethodDesc) - // or _invocationList points to a LoaderAllocator/DynamicResolver (inner open virtual delegate of a Wrapper Delegate) - // in the wrapper delegate case we want to unwrap and return the object of the inner delegate + // or _invocationList points to a LoaderAllocator/DynamicResolver OBJECTREF innerDel = thisDel->GetInvocationList(); if (innerDel != NULL) { @@ -2707,7 +2701,6 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT // 4- Static closed null first arg target method null 0 // 5- Static closed (special sig) null first arg ThisPtrRetBuf precode null 0 // 6- Static opened null delegate shuffle thunk target method 0 - // 7- Wrapper target delegate other delegate call thunk MethodDesc (frame) (arm only, VSD indirection cell address) // // Delegate invoke arg count == target method arg count - 2, 3, 6 // Delegate invoke arg count == 1 + target method arg count - 1, 4, 5 diff --git a/src/libraries/System.Private.CoreLib/src/System/Delegate.cs b/src/libraries/System.Private.CoreLib/src/System/Delegate.cs index 6efa9f48554fad..08d9966a550ea5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Delegate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Delegate.cs @@ -68,17 +68,19 @@ public abstract partial class Delegate : ICloneable, ISerializable public static Delegate CreateDelegate(Type type, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllMethods)] Type target, string method, bool ignoreCase) => CreateDelegate(type, target, method, ignoreCase, throwOnBindFailure: true)!; #if !NATIVEAOT - protected virtual Delegate CombineImpl(Delegate? d) => throw new MulticastNotSupportedException(SR.Multicast_Combine); - - protected virtual Delegate? RemoveImpl(Delegate d) => d.Equals(this) ? null : this; - - public virtual Delegate[] GetInvocationList() => [this]; - /// /// Gets a value that indicates whether the has a single invocation target. /// /// true if the has a single invocation target. - public bool HasSingleTarget => Unsafe.As(this).HasSingleTarget; + public partial bool HasSingleTarget { get; } + + public object? Target => GetTarget(); + + public virtual Delegate[] GetInvocationList() => [this]; + + protected virtual Delegate CombineImpl(Delegate? d) => throw new MulticastNotSupportedException(SR.Multicast_Combine); + + protected virtual Delegate? RemoveImpl(Delegate d) => d.Equals(this) ? null : this; #endif /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs index 99a7bc2abbf0b5..154cd64a421e88 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/Unsafe.cs @@ -72,15 +72,6 @@ public static int SizeOf() // ret } - // Internal helper that asserts the As is valid in Debug and Checked - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NotNullIfNotNull(nameof(o))] - internal static T? AsAssert(object? o) where T : class? - { - Debug.Assert(o is null or T); - return As(o); - } - /// /// Reinterprets the given reference as a reference to a value of type . /// diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index a5ba94532bf8e5..8fd99ae1c64626 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -4803,7 +4803,7 @@ public abstract partial class MulticastDelegate : System.Delegate public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } public static bool operator ==(System.MulticastDelegate? d1, System.MulticastDelegate? d2) { throw null; } public static bool operator !=(System.MulticastDelegate? d1, System.MulticastDelegate? d2) { throw null; } - protected sealed override System.Delegate? RemoveImpl(System.Delegate value) { throw null; } + protected sealed override System.Delegate? RemoveImpl(System.Delegate? value) { throw null; } } public sealed partial class MulticastNotSupportedException : System.SystemException { diff --git a/src/mono/System.Private.CoreLib/src/System/Delegate.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Delegate.Mono.cs index de3120dac128f2..86d602d6772b98 100644 --- a/src/mono/System.Private.CoreLib/src/System/Delegate.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Delegate.Mono.cs @@ -105,7 +105,7 @@ protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Al }; } - public object? Target => GetTarget(); + public partial bool HasSingleTarget => Unsafe.As(this).HasSingleTarget; internal virtual object? GetTarget() => _target; From 262032f0f41544473b2ed5524ade9380901bbb3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Sat, 20 Jun 2026 01:47:14 +0200 Subject: [PATCH 38/77] Cleanup more --- .../src/System/Delegate.CoreCLR.cs | 64 +++++++++++++++-- .../src/System/MulticastDelegate.CoreCLR.cs | 71 ------------------- .../src/System/Delegate.cs | 2 +- .../src/System/MulticastDelegate.Mono.cs | 2 +- 4 files changed, 60 insertions(+), 79 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index f8fe70a5f56b77..685778bfaaa310 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -34,7 +34,7 @@ public abstract partial class Delegate : ICloneable, ISerializable // In the case of a static method passed to a delegate, this field stores // whatever _methodPtr would have stored: and _methodPtr points to a // small thunk which removes the "this" pointer before going on - // to _methodPtr. + // to _methodPtrAux. internal nint _methodPtrAux; internal nint _invocationCount; @@ -57,6 +57,7 @@ private nuint MethodDesc public partial bool HasSingleTarget => _invocationList is not object[]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool TryGetInvocations(out ReadOnlySpan invocations) { if (_invocationList is not object[] invocationList) @@ -128,14 +129,41 @@ protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Al return invoke.Invoke(this, BindingFlags.Default, null, args, null); } - public override bool Equals([NotNullWhen(true)] object? obj) + // equals returns true IIF the delegate is not null and has the + // same target, method and invocation list as this object + public sealed override bool Equals([NotNullWhen(true)] object? obj) { - if (obj == null || !InternalEqualTypes(this, obj)) + if (obj == null) + return false; + if (ReferenceEquals(this, obj)) + return true; + if (!InternalEqualTypes(this, obj)) return false; - Debug.Assert(obj is Delegate); + // Since this is a Delegate and we know the types are the same, obj should also be a Delegate + Debug.Assert(obj is Delegate, "Shouldn't have failed here since we already checked the types are the same!"); Delegate other = Unsafe.As(obj); + if (IsMulticastOrUnmanagedOrOpenVirtual) + { + // there are 3 kind of delegate kinds that fall into this bucket + // 1- Multicast (_invocationList is Object[]) + // 2- Unmanaged FntPtr (_invocationList == null) + // 3- Open virtual (_invocationCount == MethodDesc of target, _invocationList == null, LoaderAllocator, or DynamicResolver) + if (TryGetInvocations(out ReadOnlySpan invocations)) + { + return other.TryGetInvocations(out ReadOnlySpan otherInvocations) && invocations.SequenceEqual(otherInvocations); + } + + if (IsUnmanagedFunctionPtr) + { + return other.IsUnmanagedFunctionPtr && + _methodPtr == other._methodPtr && + _methodPtrAux == other._methodPtrAux; + } + } + Debug.Assert(HasSingleTarget); + // do an optimistic check first. This is hopefully cheap enough to be worth if (_target == other._target && _methodPtr == other._methodPtr && @@ -175,8 +203,23 @@ public override bool Equals([NotNullWhen(true)] object? obj) return MethodDesc == other.MethodDesc; } - public override int GetHashCode() + public sealed override int GetHashCode() { + if (IsUnmanagedFunctionPtr) + return HashCode.Combine(_methodPtr, _methodPtr); + + if (IsMulticastOrUnmanagedOrOpenVirtual && + TryGetInvocations(out ReadOnlySpan invocations)) + { + int hash = 0; + foreach (MulticastDelegate multicastDelegate in invocations) + { + hash = hash * 33 + multicastDelegate.GetHashCode(); + } + + return hash; + } + int hashCode = MethodDesc.GetHashCode(); if (_methodPtrAux == 0 && _target != null) { @@ -185,8 +228,17 @@ public override int GetHashCode() return hashCode; } - internal virtual object? GetTarget() + private object? GetTarget() { + if (IsMulticastOrUnmanagedOrOpenVirtual) + { + // IsMulticastOrUnmanagedOrOpenVirtual we are in one of these cases: + // - Multicast -> return the target of the last delegate in the list + // - unmanaged function pointer - return null + // - virtual open delegate - return null + return TryGetInvocations(out ReadOnlySpan invocations) ? invocations[^1].Target : null; + } + return _methodPtrAux == 0 ? _target : null; } diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index ad0d5ec0a740a6..9ae0ac18ccf371 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -22,46 +22,6 @@ public override void GetObjectData(SerializationInfo info, StreamingContext cont throw new SerializationException(SR.Serialization_DelegatesNotSupported); } - // equals returns true IIF the delegate is not null and has the - // same target, method and invocation list as this object - public sealed override bool Equals([NotNullWhen(true)] object? obj) - { - if (obj == null) - return false; - if (ReferenceEquals(this, obj)) - return true; - if (!InternalEqualTypes(this, obj)) - return false; - - // Since this is a MulticastDelegate and we know - // the types are the same, obj should also be a - // MulticastDelegate - Debug.Assert(obj is MulticastDelegate, "Shouldn't have failed here since we already checked the types are the same!"); - MulticastDelegate other = Unsafe.As(obj); - - if (IsMulticastOrUnmanagedOrOpenVirtual) - { - // there are 3 kind of delegate kinds that fall into this bucket - // 1- Multicast (_invocationList is Object[]) - // 2- Unmanaged FntPtr (_invocationList == null) - // 3- Open virtual (_invocationCount == MethodDesc of target, _invocationList == null, LoaderAllocator, or DynamicResolver) - if (TryGetInvocations(out ReadOnlySpan invocations)) - { - return other.TryGetInvocations(out ReadOnlySpan otherInvocations) && invocations.SequenceEqual(otherInvocations); - } - - if (IsUnmanagedFunctionPtr) - { - return other.IsUnmanagedFunctionPtr && - _methodPtr == other._methodPtr && - _methodPtrAux == other._methodPtrAux; - } - } - - Debug.Assert(HasSingleTarget); - return base.Equals(other); - } - private static bool TrySetSlot(object?[] a, int index, object o) { if (a[index] == null && Interlocked.CompareExchange(ref a[index], o, null) == null) @@ -331,37 +291,6 @@ public sealed override Delegate[] GetInvocationList() return null; } - public sealed override int GetHashCode() - { - if (IsUnmanagedFunctionPtr) - return HashCode.Combine(_methodPtr, _methodPtr); - - if (!IsMulticastOrUnmanagedOrOpenVirtual || !TryGetInvocations(out ReadOnlySpan invocations)) - { - return base.GetHashCode(); - } - - int hash = 0; - foreach (MulticastDelegate multicastDelegate in invocations) - { - hash = hash * 33 + multicastDelegate.GetHashCode(); - } - return hash; - } - - internal override object? GetTarget() - { - if (IsMulticastOrUnmanagedOrOpenVirtual) - { - // IsMulticastOrUnmanagedOrOpenVirtual we are in one of these cases: - // - Multicast -> return the target of the last delegate in the list - // - unmanaged function pointer - return null - // - virtual open delegate - return null - return TryGetInvocations(out ReadOnlySpan invocations) ? invocations[^1].Target : null; - } - return base.GetTarget(); - } - // this should help inlining [DoesNotReturn] [DebuggerNonUserCode] diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs index 9968de32d01574..6e0b34b188301b 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs @@ -602,7 +602,7 @@ private static bool EqualInvocationLists(Wrapper[] a, Wrapper[] b, int start, in // look at the invocation list.) If this is found we remove it from // this list and return a new delegate. If its not found a copy of the // current list is returned. - protected virtual Delegate? RemoveImpl(Delegate d) + protected virtual Delegate? RemoveImpl(Delegate? d) { // There is a special case were we are removing using a delegate as // the value we need to check for this case diff --git a/src/mono/System.Private.CoreLib/src/System/MulticastDelegate.Mono.cs b/src/mono/System.Private.CoreLib/src/System/MulticastDelegate.Mono.cs index 47fb79aa3cdfa7..240e9f281bdf41 100644 --- a/src/mono/System.Private.CoreLib/src/System/MulticastDelegate.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/MulticastDelegate.Mono.cs @@ -197,7 +197,7 @@ private static int LastIndexOf(Delegate[] haystack, Delegate[] needle) return -1; } - protected sealed override Delegate? RemoveImpl(Delegate value) + protected sealed override Delegate? RemoveImpl(Delegate? value) { if (value == null) return this; From 018bc6ae85dcc533eda325596db5ffb01b7f8edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Sat, 20 Jun 2026 02:15:39 +0200 Subject: [PATCH 39/77] Follow the same pattern as the other places --- .../src/System/Delegate.CoreCLR.cs | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 685778bfaaa310..0c3d40389c169c 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -205,19 +205,22 @@ public sealed override bool Equals([NotNullWhen(true)] object? obj) public sealed override int GetHashCode() { - if (IsUnmanagedFunctionPtr) - return HashCode.Combine(_methodPtr, _methodPtr); - - if (IsMulticastOrUnmanagedOrOpenVirtual && - TryGetInvocations(out ReadOnlySpan invocations)) + if (IsMulticastOrUnmanagedOrOpenVirtual) { - int hash = 0; - foreach (MulticastDelegate multicastDelegate in invocations) + if (TryGetInvocations(out ReadOnlySpan invocations)) { - hash = hash * 33 + multicastDelegate.GetHashCode(); + int hash = 0; + foreach (MulticastDelegate multicastDelegate in invocations) + { + hash = hash * 33 + multicastDelegate.GetHashCode(); + } + return hash; } - return hash; + if (IsUnmanagedFunctionPtr) + { + return HashCode.Combine(_methodPtr, _methodPtr); + } } int hashCode = MethodDesc.GetHashCode(); From be611788ddb3e9c73181814ac0786d877ee52297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Sat, 20 Jun 2026 03:00:48 +0200 Subject: [PATCH 40/77] Fix build --- .../System.Private.CoreLib/src/System/Delegate.CoreCLR.cs | 4 ++-- .../src/System/MulticastDelegate.CoreCLR.cs | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 0c3d40389c169c..ab4f80172a6304 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -131,7 +131,7 @@ protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Al // equals returns true IIF the delegate is not null and has the // same target, method and invocation list as this object - public sealed override bool Equals([NotNullWhen(true)] object? obj) + public override bool Equals([NotNullWhen(true)] object? obj) { if (obj == null) return false; @@ -203,7 +203,7 @@ public sealed override bool Equals([NotNullWhen(true)] object? obj) return MethodDesc == other.MethodDesc; } - public sealed override int GetHashCode() + public override int GetHashCode() { if (IsMulticastOrUnmanagedOrOpenVirtual) { diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index 9ae0ac18ccf371..1528b560ac191e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -291,6 +291,9 @@ public sealed override Delegate[] GetInvocationList() return null; } + public sealed override bool Equals([NotNullWhen(true)] object? obj) => base.Equals(obj); + public sealed override int GetHashCode() => base.GetHashCode(); + // this should help inlining [DoesNotReturn] [DebuggerNonUserCode] From 963134821dad1307f560279543ffb8c4aed257df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Sat, 20 Jun 2026 03:11:23 +0200 Subject: [PATCH 41/77] Cleanup native logic --- src/coreclr/vm/comdelegate.cpp | 65 ++-------------------------------- src/coreclr/vm/comdelegate.h | 1 - 2 files changed, 3 insertions(+), 63 deletions(-) diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 94de384a63b5e4..cd2b8d37e89057 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1827,36 +1827,15 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) { // this is one of the following: // - multicast - _invocationList is Array && _invocationCount != 0 - // - unamanaged ftn ptr - _invocationList == NULL && _invocationCount == -1 + // - unmanaged ftn ptr - _invocationList == NULL && _invocationCount == -1 // - virtual delegate - _invocationList == null && _invocationCount == (target MethodDesc) // or _invocationList points to a LoaderAllocator/DynamicResolver // we return the method desc for the invoke - bool fOpenVirtualDelegate = false; OBJECTREF innerDel = thisDel->GetInvocationList(); - if (innerDel != NULL) - { - MethodTable *pMT = innerDel->GetMethodTable(); - if (pMT->IsDelegate()) - return GetMethodDesc(innerDel); - if (!pMT->IsArray()) - { - // must be a virtual one - fOpenVirtualDelegate = true; - } - } + if (innerDel != NULL && innerDel->GetMethodTable()->IsArray() || count == DELEGATE_MARKER_UNMANAGEDFPTR) + pMethodHandle = FindDelegateInvokeMethod(thisDel->GetMethodTable()); else - { - if (count != DELEGATE_MARKER_UNMANAGEDFPTR) - { - // must be a virtual one - fOpenVirtualDelegate = true; - } - } - - if (fOpenVirtualDelegate) pMethodHandle = GetMethodDescForOpenVirtualDelegate(thisDel); - else - pMethodHandle = FindDelegateInvokeMethod(thisDel->GetMethodTable()); } else { @@ -1879,44 +1858,6 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) return pMethodHandle; } -OBJECTREF COMDelegate::GetTargetObject(OBJECTREF obj) -{ - CONTRACTL - { - THROWS; - GC_NOTRIGGER; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - OBJECTREF targetObject = NULL; - - DELEGATEREF thisDel = (DELEGATEREF) obj; - - if (thisDel->GetInvocationCount() != 0) - { - // this is one of the following: - // - multicast - // - unmanaged ftn ptr - // - virtual delegate - _invocationList == null && _invocationCount == (target MethodDesc) - // or _invocationList points to a LoaderAllocator/DynamicResolver - OBJECTREF innerDel = thisDel->GetInvocationList(); - if (innerDel != NULL) - { - MethodTable *pMT = innerDel->GetMethodTable(); - if (pMT->IsDelegate()) - { - targetObject = GetTargetObject(innerDel); - } - } - } - - if (targetObject == NULL) - targetObject = thisDel->GetTarget(); - - return targetObject; -} - BOOL COMDelegate::IsTrueMulticastDelegate(OBJECTREF delegate) { CONTRACTL diff --git a/src/coreclr/vm/comdelegate.h b/src/coreclr/vm/comdelegate.h index 41379ac3c4eaff..07534d10f7a879 100644 --- a/src/coreclr/vm/comdelegate.h +++ b/src/coreclr/vm/comdelegate.h @@ -60,7 +60,6 @@ class COMDelegate static MethodDesc * __fastcall GetMethodDesc(OBJECTREF obj); static MethodDesc* GetMethodDescForOpenVirtualDelegate(OBJECTREF orDelegate); - static OBJECTREF GetTargetObject(OBJECTREF obj); static BOOL IsTrueMulticastDelegate(OBJECTREF delegate); From 5f121f3034ca95610c0d1319deeb3c744a1a1f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Sat, 20 Jun 2026 03:49:29 +0200 Subject: [PATCH 42/77] Fix build --- src/coreclr/vm/comdelegate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index cd2b8d37e89057..173db2449a6f47 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1832,7 +1832,7 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) // or _invocationList points to a LoaderAllocator/DynamicResolver // we return the method desc for the invoke OBJECTREF innerDel = thisDel->GetInvocationList(); - if (innerDel != NULL && innerDel->GetMethodTable()->IsArray() || count == DELEGATE_MARKER_UNMANAGEDFPTR) + if ((innerDel != NULL && innerDel->GetMethodTable()->IsArray()) || count == DELEGATE_MARKER_UNMANAGEDFPTR) pMethodHandle = FindDelegateInvokeMethod(thisDel->GetMethodTable()); else pMethodHandle = GetMethodDescForOpenVirtualDelegate(thisDel); From 7c103a112d926b1e45fd1ae1555984acb55da6d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Sat, 20 Jun 2026 22:46:00 +0200 Subject: [PATCH 43/77] Move methodDesc to invocationCount --- .../src/System/Delegate.CoreCLR.cs | 99 ++++++++----------- .../src/System/MulticastDelegate.CoreCLR.cs | 2 +- src/coreclr/debug/daccess/dacdbiimpl.cpp | 5 +- src/coreclr/vm/comdelegate.cpp | 87 ++++++---------- src/coreclr/vm/comdelegate.h | 1 - src/coreclr/vm/corelib.h | 1 - src/coreclr/vm/interpexec.cpp | 2 +- src/coreclr/vm/jithelpers.cpp | 44 ++++++--- src/coreclr/vm/object.h | 7 +- src/coreclr/vm/stubmgr.cpp | 47 ++------- src/coreclr/vm/virtualcallstub.cpp | 2 +- 11 files changed, 112 insertions(+), 185 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index ab4f80172a6304..e9eabefd802a2a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -39,33 +39,31 @@ public abstract partial class Delegate : ICloneable, ISerializable internal nint _invocationCount; - // VM handle to wrapped method, null when not initialized - private nuint _methodDesc; - private nuint MethodDesc { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - return _methodDesc != 0 ? _methodDesc : GetMethodDesc(); + Debug.Assert(HasSingleTarget); + Debug.Assert(!IsUnmanagedFunctionPtr); + return _invocationCount != 0 ? (nuint)_invocationCount : GetMethodDesc(); } } - internal bool IsMulticastOrUnmanagedOrOpenVirtual => _invocationCount != 0; - internal bool IsUnmanagedFunctionPtr => _invocationCount == -1; - public partial bool HasSingleTarget => _invocationList is not object[]; + public partial bool HasSingleTarget => _invocationList is null || _invocationList.GetType() != typeof(object[]); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool TryGetInvocations(out ReadOnlySpan invocations) { - if (_invocationList is not object[] invocationList) + if (HasSingleTarget) { invocations = default; return false; } + object[] invocationList = (object[])_invocationList!; Debug.Assert(invocationList.Length > 1); Debug.Assert(invocationList[0] is MulticastDelegate); Debug.Assert((uint)invocationList.Length >= (nuint)_invocationCount); @@ -144,25 +142,17 @@ public override bool Equals([NotNullWhen(true)] object? obj) Debug.Assert(obj is Delegate, "Shouldn't have failed here since we already checked the types are the same!"); Delegate other = Unsafe.As(obj); - if (IsMulticastOrUnmanagedOrOpenVirtual) + if (TryGetInvocations(out ReadOnlySpan invocations)) { - // there are 3 kind of delegate kinds that fall into this bucket - // 1- Multicast (_invocationList is Object[]) - // 2- Unmanaged FntPtr (_invocationList == null) - // 3- Open virtual (_invocationCount == MethodDesc of target, _invocationList == null, LoaderAllocator, or DynamicResolver) - if (TryGetInvocations(out ReadOnlySpan invocations)) - { - return other.TryGetInvocations(out ReadOnlySpan otherInvocations) && invocations.SequenceEqual(otherInvocations); - } + return other.TryGetInvocations(out ReadOnlySpan otherInvocations) && invocations.SequenceEqual(otherInvocations); + } - if (IsUnmanagedFunctionPtr) - { - return other.IsUnmanagedFunctionPtr && - _methodPtr == other._methodPtr && - _methodPtrAux == other._methodPtrAux; - } + if (IsUnmanagedFunctionPtr) + { + return other.IsUnmanagedFunctionPtr && + _methodPtr == other._methodPtr && + _methodPtrAux == other._methodPtrAux; } - Debug.Assert(HasSingleTarget); // do an optimistic check first. This is hopefully cheap enough to be worth if (_target == other._target && @@ -205,22 +195,19 @@ public override bool Equals([NotNullWhen(true)] object? obj) public override int GetHashCode() { - if (IsMulticastOrUnmanagedOrOpenVirtual) + if (TryGetInvocations(out ReadOnlySpan invocations)) { - if (TryGetInvocations(out ReadOnlySpan invocations)) + int hash = 0; + foreach (MulticastDelegate multicastDelegate in invocations) { - int hash = 0; - foreach (MulticastDelegate multicastDelegate in invocations) - { - hash = hash * 33 + multicastDelegate.GetHashCode(); - } - return hash; + hash = hash * 33 + multicastDelegate.GetHashCode(); } + return hash; + } - if (IsUnmanagedFunctionPtr) - { - return HashCode.Combine(_methodPtr, _methodPtr); - } + if (IsUnmanagedFunctionPtr) + { + return HashCode.Combine(_methodPtr, _methodPtr); } int hashCode = MethodDesc.GetHashCode(); @@ -233,16 +220,9 @@ public override int GetHashCode() private object? GetTarget() { - if (IsMulticastOrUnmanagedOrOpenVirtual) - { - // IsMulticastOrUnmanagedOrOpenVirtual we are in one of these cases: - // - Multicast -> return the target of the last delegate in the list - // - unmanaged function pointer - return null - // - virtual open delegate - return null - return TryGetInvocations(out ReadOnlySpan invocations) ? invocations[^1].Target : null; - } - - return _methodPtrAux == 0 ? _target : null; + return TryGetInvocations(out ReadOnlySpan invocations) + ? invocations[^1].Target + : !IsUnmanagedFunctionPtr && _methodPtrAux == 0 ? _target : null; } protected virtual MethodInfo GetMethodImpl() @@ -269,27 +249,23 @@ private MethodInfo GetMethodImplCore() // should be handled by GetMethodImpl Debug.Assert(HasSingleTarget); - IRuntimeMethodInfo method = CreateMethodInfo(MethodDesc); + IRuntimeMethodInfo method = CreateMethodInfo(IsUnmanagedFunctionPtr ? GetMethodDesc() : MethodDesc); RuntimeType? declaringType = RuntimeMethodHandle.GetDeclaringType(method); - if (IsUnmanagedFunctionPtr) + // need a proper declaring type instance method on a generic type + if (declaringType.IsGenericType) { - // we handle unmanaged function pointers here because the generic ones (used for WinRT) would otherwise - // be treated as open delegates, resulting in failure to get the MethodInfo - - // need a proper declaring type instance method on a generic type - if (declaringType.IsGenericType) + bool isStatic = (RuntimeMethodHandle.GetAttributes(method) & MethodAttributes.Static) != 0; + if (IsUnmanagedFunctionPtr) { + // we handle unmanaged function pointers here because the generic ones (used for WinRT) would otherwise + // be treated as open delegates, resulting in failure to get the MethodInfo + // we are returning the 'Invoke' method of this delegate so use this.GetType() for the exact type RuntimeType reflectedType = (RuntimeType)GetType(); declaringType = reflectedType; } - } - // need a proper declaring type instance method on a generic type - else if (declaringType.IsGenericType) - { - bool isStatic = (RuntimeMethodHandle.GetAttributes(method) & MethodAttributes.Static) != 0; - if (!isStatic) + else if (!isStatic) { if (_methodPtrAux == 0) { @@ -634,7 +610,10 @@ private nuint GetMethodDesc() { Delegate d = this; nuint desc = GetMethodDesc(ObjectHandleOnStack.Create(ref d)); - _methodDesc = desc; + if (!IsUnmanagedFunctionPtr) + { + _invocationCount = (nint)desc; + } return desc; } diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index 1528b560ac191e..b53c77947a540f 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -276,7 +276,7 @@ public sealed override Delegate[] GetInvocationList() } // Used by delegate invocation list enumerator - internal MulticastDelegate? TryGetAt(int index) + internal Delegate? TryGetAt(int index) { if (TryGetInvocations(out ReadOnlySpan invocations)) { diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index adb057fba3011a..c71e0302ffe628 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -3262,9 +3262,10 @@ DacDbiInterfaceImpl::DelegateType DacDbiInterfaceImpl::GetDelegateType(VMPTR_Obj DelegateType delegateType = DelegateType::kUnknownDelegateType; PTR_DelegateObject pDelObj = dac_cast(delegateObject.GetDacPtr()); - INT_PTR invocationCount = pDelObj->GetInvocationCount(); - if (invocationCount == 0) + INT_PTR invocationCount = pDelObj->GetInvocationCount(); + OBJECTREF invocationList = pDelObj->GetInvocationList(); + if (((invocationList == NULL) || !invocationList->GetMethodTable()->IsArray()) && (invocationCount != DELEGATE_MARKER_UNMANAGEDFPTR)) { // If this delegate points to a static function or this is a open virtual delegate, this should be non-null // Special case: This might fail in a VSD delegate (instance open virtual)... diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 173db2449a6f47..47e7bc7a5d7d51 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -26,8 +26,6 @@ #include "comcallablewrapper.h" #endif // FEATURE_COMINTEROP -#define DELEGATE_MARKER_UNMANAGEDFPTR (-1) - #ifndef DACCESS_COMPILE #if defined(TARGET_X86) @@ -1230,7 +1228,6 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, // runtime. PCODE pTargetCall = GetVirtualCallStub(pTargetMethod, TypeHandle(pExactMethodType)); (*pRefThis)->SetMethodPtrAux(pTargetCall); - (*pRefThis)->SetInvocationCount((INT_PTR)(void *)pTargetMethod); } else { @@ -1260,7 +1257,7 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, } else { - PCODE pTargetCode = (PCODE)NULL; + PCODE pTargetCode = NULL; // For virtual methods we can (and should) virtualize the call now (so we don't have to insert a thunk to do so at runtime). // @@ -1290,6 +1287,8 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, (*pRefThis)->SetMethodPtr(pTargetCode); } + (*pRefThis)->SetInvocationCount((INT_PTR)pTargetMethod); + LoaderAllocator *pLoaderAllocator = pTargetMethod->GetLoaderAllocator(); _ASSERTE((*pRefThis)->GetInvocationList() == NULL); @@ -1727,7 +1726,6 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q { PCODE pTargetCall = GetVirtualCallStub(pMeth, TypeHandle(pMeth->GetMethodTable())); refThis->SetMethodPtrAux(pTargetCall); - refThis->SetInvocationCount((INT_PTR)(void *)pMeth); } else { @@ -1784,23 +1782,12 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q refThis->SetMethodPtr((PCODE)(void *)method); } + refThis->SetInvocationCount((INT_PTR)pMeth); + GCPROTECT_END(); END_QCALL; } -MethodDesc *COMDelegate::GetMethodDescForOpenVirtualDelegate(OBJECTREF orDelegate) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - return (MethodDesc*)((DELEGATEREF)orDelegate)->GetInvocationCount(); -} - MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) { CONTRACTL @@ -1815,46 +1802,34 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) DELEGATEREF thisDel = (DELEGATEREF) orDelegate; - MethodDesc *pMethodHandle = thisDel->GetMethodDesc(); - - if (pMethodHandle != NULL) - { - return pMethodHandle; - } - INT_PTR count = thisDel->GetInvocationCount(); if (count != 0) { // this is one of the following: // - multicast - _invocationList is Array && _invocationCount != 0 // - unmanaged ftn ptr - _invocationList == NULL && _invocationCount == -1 - // - virtual delegate - _invocationList == null && _invocationCount == (target MethodDesc) - // or _invocationList points to a LoaderAllocator/DynamicResolver + // - MethodDesc already cached + // we return the method desc for the invoke OBJECTREF innerDel = thisDel->GetInvocationList(); if ((innerDel != NULL && innerDel->GetMethodTable()->IsArray()) || count == DELEGATE_MARKER_UNMANAGEDFPTR) - pMethodHandle = FindDelegateInvokeMethod(thisDel->GetMethodTable()); - else - pMethodHandle = GetMethodDescForOpenVirtualDelegate(thisDel); - } - else - { - // Next, check for an open delegate - PCODE code = thisDel->GetMethodPtrAux(); + return FindDelegateInvokeMethod(thisDel->GetMethodTable()); - if (code == (PCODE)NULL) - { - // Must be a normal delegate - code = thisDel->GetMethodPtr(); - } + return (MethodDesc*)count; + } - pMethodHandle = NonVirtualEntry2MethodDesc(code); + // Next, check for an open delegate + PCODE code = thisDel->GetMethodPtrAux(); + if (code == NULL) + { + // Must be a normal delegate + code = thisDel->GetMethodPtr(); } + MethodDesc* pMethodHandle = NonVirtualEntry2MethodDesc(code); _ASSERTE(pMethodHandle); - thisDel->SetMethodDesc(pMethodHandle); - + thisDel->SetInvocationCount((INT_PTR)pMethodHandle); return pMethodHandle; } @@ -1870,15 +1845,11 @@ BOOL COMDelegate::IsTrueMulticastDelegate(OBJECTREF delegate) BOOL isMulticast = FALSE; - size_t invocationCount = ((DELEGATEREF)delegate)->GetInvocationCount(); - if (invocationCount) + OBJECTREF invocationList = ((DELEGATEREF)delegate)->GetInvocationList(); + if (invocationList != NULL) { - OBJECTREF invocationList = ((DELEGATEREF)delegate)->GetInvocationList(); - if (invocationList != NULL) - { - MethodTable *pMT = invocationList->GetMethodTable(); - isMulticast = pMT->IsArray(); - } + MethodTable *pMT = invocationList->GetMethodTable(); + isMulticast = pMT->IsArray(); } return isMulticast; @@ -2634,14 +2605,14 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT // DELEGATE KINDS TABLE // - // _invocationList _target _methodPtr _methodPtrAux _invocationCount + // _invocationList _target _methodPtr _methodPtrAux // - // 1- Instance closed null 'this' ptr target method null 0 - // 2- Instance open non-virt null delegate shuffle thunk target method 0 - // 3- Instance open virtual null delegate Virtual-stub dispatch method id 0 - // 4- Static closed null first arg target method null 0 - // 5- Static closed (special sig) null first arg ThisPtrRetBuf precode null 0 - // 6- Static opened null delegate shuffle thunk target method 0 + // 1- Instance closed null 'this' ptr target method null + // 2- Instance open non-virt null delegate shuffle thunk target method + // 3- Instance open virtual null delegate Virtual-stub dispatch method id + // 4- Static closed null first arg target method null + // 5- Static closed (special sig) null first arg ThisPtrRetBuf precode null + // 6- Static opened null delegate shuffle thunk target method // // Delegate invoke arg count == target method arg count - 2, 3, 6 // Delegate invoke arg count == 1 + target method arg count - 1, 4, 5 diff --git a/src/coreclr/vm/comdelegate.h b/src/coreclr/vm/comdelegate.h index 07534d10f7a879..fe82249726fb9d 100644 --- a/src/coreclr/vm/comdelegate.h +++ b/src/coreclr/vm/comdelegate.h @@ -59,7 +59,6 @@ class COMDelegate static Stub* GetInvokeMethodStub(EEImplMethodDesc* pMD); static MethodDesc * __fastcall GetMethodDesc(OBJECTREF obj); - static MethodDesc* GetMethodDescForOpenVirtualDelegate(OBJECTREF orDelegate); static BOOL IsTrueMulticastDelegate(OBJECTREF delegate); diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 6b230e60557374..5bef79a5aa4ac7 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -233,7 +233,6 @@ DEFINE_FIELD_U(_target, DelegateObject, _target) DEFINE_FIELD_U(_methodPtr, DelegateObject, _methodPtr) DEFINE_FIELD_U(_methodPtrAux, DelegateObject, _methodPtrAux) DEFINE_FIELD_U(_invocationCount, DelegateObject, _invocationCount) -DEFINE_FIELD_U(_methodDesc, DelegateObject, _methodDesc) DEFINE_CLASS(DELEGATE, System, Delegate) DEFINE_FIELD(DELEGATE, INVOCATION_LIST, _invocationList) DEFINE_FIELD(DELEGATE, TARGET, _target) diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 714f41bfa7a742..574022575ad2ab 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -3323,7 +3323,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr if (isOpenVirtual) { - targetMethod = COMDelegate::GetMethodDescForOpenVirtualDelegate(*delegateObj); + targetMethod = (MethodDesc*)(*delegateObj)->GetInvocationCount(); OBJECTREF *pThisArg = LOCAL_VAR_ADDR(callArgsOffset + INTERP_STACK_SLOT_SIZE, OBJECTREF); NULL_CHECK(*pThisArg); targetMethod = CallWithSEHWrapper( diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index a389d56231ba0c..10b76b3a62ef26 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1903,16 +1903,22 @@ HCIMPL2(void, JIT_DelegateProfile32, Object *obj, ICorJitInfo::HandleHistogram32 _ASSERTE(pMT->IsDelegate()); // Resolve method. We handle only the common "direct" delegate as that is - // in any case the only one we can reasonably do GDV for. For instance, - // open delegates are filtered out here, and many cases with inner - // "complicated" logic as well (e.g. static functions, multicast, unmanaged - // functions). - // - MethodDesc* pRecordedMD = (MethodDesc*)DEFAULT_UNKNOWN_HANDLE; + // in any case the only one we can reasonably do GDV for. + // We filter out multicast and unmanaged here. + DELEGATEREF del = (DELEGATEREF)objRef; - if ((del->GetInvocationCount() == 0) && (del->GetMethodPtrAux() == (PCODE)NULL)) + INT_PTR invocationCount = del->GetInvocationCount(); + OBJECTREF invocationList = del->GetInvocationList(); + + MethodDesc* pRecordedMD = (MethodDesc*)DEFAULT_UNKNOWN_HANDLE; + if (((invocationList == NULL) || !invocationList->GetMethodTable()->IsArray()) && (invocationCount != DELEGATE_MARKER_UNMANAGEDFPTR)) { - MethodDesc* pMD = NonVirtualEntry2MethodDesc(del->GetMethodPtr()); + MethodDesc* pMD = (MethodDesc*)invocationCount; + if ((pMD == nullptr) && (del->GetMethodPtrAux() == NULL)) + { + pMD = NonVirtualEntry2MethodDesc(del->GetMethodPtr()); + } + if ((pMD != nullptr) && !pMD->GetLoaderAllocator()->IsCollectible() && !pMD->IsDynamicMethod()) { pRecordedMD = pMD; @@ -1949,16 +1955,22 @@ HCIMPL2(void, JIT_DelegateProfile64, Object *obj, ICorJitInfo::HandleHistogram64 _ASSERTE(pMT->IsDelegate()); // Resolve method. We handle only the common "direct" delegate as that is - // in any case the only one we can reasonably do GDV for. For instance, - // open delegates are filtered out here, and many cases with inner - // "complicated" logic as well (e.g. static functions, multicast, unmanaged - // functions). - // - MethodDesc* pRecordedMD = (MethodDesc*)DEFAULT_UNKNOWN_HANDLE; + // in any case the only one we can reasonably do GDV for. + // We filter out multicast and unmanaged here. + DELEGATEREF del = (DELEGATEREF)objRef; - if ((del->GetInvocationCount() == 0) && (del->GetMethodPtrAux() == (PCODE)NULL)) + INT_PTR invocationCount = del->GetInvocationCount(); + OBJECTREF invocationList = del->GetInvocationList(); + + MethodDesc* pRecordedMD = (MethodDesc*)DEFAULT_UNKNOWN_HANDLE; + if (((invocationList == NULL) || !invocationList->GetMethodTable()->IsArray()) && (invocationCount != DELEGATE_MARKER_UNMANAGEDFPTR)) { - MethodDesc* pMD = NonVirtualEntry2MethodDesc(del->GetMethodPtr()); + MethodDesc* pMD = (MethodDesc*)invocationCount; + if ((pMD == nullptr) && (del->GetMethodPtrAux() == NULL)) + { + pMD = NonVirtualEntry2MethodDesc(del->GetMethodPtr()); + } + if ((pMD != nullptr) && !pMD->GetLoaderAllocator()->IsCollectible() && !pMD->IsDynamicMethod()) { pRecordedMD = pMD; diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index 5d7c0e1e77bbcd..e5544385be0f00 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -1743,6 +1743,7 @@ typedef BStrWrapper* BSTRWRAPPEROBJECTREF; #endif // FEATURE_COMINTEROP +#define DELEGATE_MARKER_UNMANAGEDFPTR (-1) // This class corresponds to System.Delegate on the managed side. class DelegateObject : public Object @@ -1772,10 +1773,6 @@ class DelegateObject : public Object void SetInvocationCount(INT_PTR invocationCount) { LIMITED_METHOD_CONTRACT; _invocationCount = invocationCount; } static int GetOffsetOfInvocationCount() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _invocationCount); } - MethodDesc* GetMethodDesc() { LIMITED_METHOD_CONTRACT; return _methodDesc; } - void SetMethodDesc(MethodDesc* methodDesc) { LIMITED_METHOD_CONTRACT; _methodDesc = methodDesc; } - static int GetOffsetOfMethodDesc() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodDesc); } - // README: // If you modify the order of these fields, make sure to update the definition in // BCL for this object. @@ -1785,13 +1782,11 @@ class DelegateObject : public Object PCODE _methodPtr; PCODE _methodPtrAux; INT_PTR _invocationCount; - MethodDesc* _methodDesc; }; #define OFFSETOF__DelegateObject__target (OBJECT_SIZE /* m_pMethTab */ + TARGET_POINTER_SIZE /* _invocationList */) #define OFFSETOF__DelegateObject__methodPtr (OFFSETOF__DelegateObject__target + TARGET_POINTER_SIZE /* _target */) #define OFFSETOF__DelegateObject__methodPtrAux (OFFSETOF__DelegateObject__methodPtr + TARGET_POINTER_SIZE /* _methodPtr */) -#define OFFSETOF__DelegateObject__methodDesc (OFFSETOF__DelegateObject__methodPtrAux + TARGET_POINTER_SIZE /* _methodPtrAux */ + TARGET_POINTER_SIZE /* _invocationCount */) template<> struct cdac_data diff --git a/src/coreclr/vm/stubmgr.cpp b/src/coreclr/vm/stubmgr.cpp index e8f55803ece74a..3d79f096d71d36 100644 --- a/src/coreclr/vm/stubmgr.cpp +++ b/src/coreclr/vm/stubmgr.cpp @@ -1212,13 +1212,17 @@ BOOL StubLinkStubManager::TraceDelegateObject(BYTE* pbDel, TraceDestination *tra LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: invocationList: %p\n", pbDelInvocationList)); - if (pbDelInvocationList == NULL) + if (pbDelInvocationList == NULL || !(*(MethodTable**)pbDelInvocationList)->IsArray()) { // A null invocationList can be one of the following: - // - Instance closed, Instance open non-virt, Instance open virtual, Static closed, Static opened, Unmanaged FtnPtr - // - Instance open virtual is complex and we need to figure out what to do (TODO). - // For the others the logic is the following: - // if _methodPtrAux is 0 the target is in _methodPtr, otherwise the taret is _methodPtrAux + // - Instance closed + // - Instance open non-virt + // - Instance open virtual + // - Static closed + // - Static opened + // - Instance open virtual + // - Unmanaged FtnPtr + // if _methodPtrAux is 0 the target is in _methodPtr, otherwise the target is _methodPtrAux ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtrAux()); if (*ppbDest == NULL) @@ -1242,39 +1246,6 @@ BOOL StubLinkStubManager::TraceDelegateObject(BYTE* pbDel, TraceDestination *tra return res; } - // invocationList is not null, so it can be one of the following: - // Multicast, Static closed (special sig), Secure - - // rule out the static with special sig - BYTE *pbCount = *(BYTE **)(pbDel + DelegateObject::GetOffsetOfInvocationCount()); - if (pbCount == NULL) - { - // it's a static closed, the target lives in _methodAuxPtr - ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtrAux()); - - if (*ppbDest == NULL) - { - // it's not looking good, bail out - LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: can't trace into it\n")); - return FALSE; - } - - LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: ppbDest: %p *ppbDest:%p\n", ppbDest, *ppbDest)); - - BOOL res = StubManager::TraceStub((PCODE) (*ppbDest), trace); - - LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: res: %d, result type: %d\n", (res ? "true" : "false"), trace->GetTraceType())); - - return res; - } - - MethodTable *pType = *(MethodTable**)pbDelInvocationList; - if (pType->IsDelegate()) - { - // this is a secure delegate. The target is hidden inside this field, so recurse. - return TraceDelegateObject(pbDelInvocationList, trace); - } - // Otherwise, we're going for the first invoke of the multi case. // In order to go to the correct spot, we have just have to fish out // slot 0 of the invocation list, and figure out where that's going to, diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index 0b1567592843c8..e3faa10a51b1d8 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -1475,7 +1475,7 @@ extern "C" PCODE CID_VirtualOpenDelegateDispatchWorker(TransitionBlock * pTransi _ASSERTE(!"Throw returned"); } - MethodDesc *pTargetMD = COMDelegate::GetMethodDescForOpenVirtualDelegate(delegateObj); + MethodDesc *pTargetMD = (MethodDesc*)((DELEGATEREF)delegateObj)->GetInvocationCount(); pSDFrame->SetFunction(pTargetMD); pSDFrame->Push(CURRENT_THREAD); From 474c91615d36fb80dc2a7eaefaf6bfff772b1aa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 20 Jun 2026 23:05:02 +0200 Subject: [PATCH 44/77] Cleanup properties --- .../src/System/Delegate.CoreCLR.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index e9eabefd802a2a..6e3fc24efbfce1 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -17,6 +17,8 @@ namespace System [NonVersionable] public abstract partial class Delegate : ICloneable, ISerializable { + private const nint UnmanagedMarker = -1; + // This is set under 3 circumstances // 1. Multicast delegate // 2. Method cache @@ -37,8 +39,13 @@ public abstract partial class Delegate : ICloneable, ISerializable // to _methodPtrAux. internal nint _methodPtrAux; + // this stores the multicast count, UnmanagedMarker for unmanaged or target MethodDesc internal nint _invocationCount; + internal bool IsUnmanagedFunctionPtr => _invocationCount == UnmanagedMarker; + + public partial bool HasSingleTarget => _invocationList is null || _invocationList.GetType() != typeof(object[]); + private nuint MethodDesc { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -50,10 +57,6 @@ private nuint MethodDesc } } - internal bool IsUnmanagedFunctionPtr => _invocationCount == -1; - - public partial bool HasSingleTarget => _invocationList is null || _invocationList.GetType() != typeof(object[]); - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool TryGetInvocations(out ReadOnlySpan invocations) { From 2bafecdcc62c53d0d12bee28f9d5f5c505442403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Mon, 29 Jun 2026 01:40:56 +0200 Subject: [PATCH 45/77] Fixes, cleanup, address feedback --- docs/design/datacontracts/Object.md | 25 ++-- .../src/System/Delegate.CoreCLR.cs | 91 +++++++++------ .../src/System/MulticastDelegate.CoreCLR.cs | 110 +++++++----------- src/coreclr/debug/daccess/dacdbiimpl.cpp | 19 +-- src/coreclr/debug/ee/controller.cpp | 2 +- src/coreclr/vm/comdelegate.cpp | 46 ++++---- src/coreclr/vm/comdelegate.h | 2 +- src/coreclr/vm/corelib.h | 28 ++--- .../vm/datadescriptor/datadescriptor.inc | 3 +- src/coreclr/vm/interpexec.cpp | 2 +- src/coreclr/vm/jithelpers.cpp | 14 +-- src/coreclr/vm/object.h | 17 +-- src/coreclr/vm/stubmgr.cpp | 8 +- src/coreclr/vm/virtualcallstub.cpp | 2 +- .../src/System/Delegate.cs | 6 +- .../src/System/Delegate.Mono.cs | 6 + .../Contracts/Object_1.cs | 21 ++-- .../Data/Delegate.cs | 5 +- 18 files changed, 203 insertions(+), 204 deletions(-) diff --git a/docs/design/datacontracts/Object.md b/docs/design/datacontracts/Object.md index f0f6f107748e96..a533e3e2587971 100644 --- a/docs/design/datacontracts/Object.md +++ b/docs/design/datacontracts/Object.md @@ -220,15 +220,24 @@ DelegateInfo GetDelegateInfo(TargetPointer address) { Data.Delegate del = new Data.Delegate(target, address); - // Classify the delegate from its invocation count and auxiliary pointer. - // This does not handle open virtual delegates correctly. - DelegateType delegateType = target.ReadNInt(address + /* Delegate::InvocationCount offset */) switch + // Check for multicast and unmanaged first. + bool isMulticast = false; + TargetPointer helperObject = target.ReadPointer(address + /* Delegate::HelperObject offset */); + if (helperObject == TargetPointer.Null) { - 0 => del.MethodPtrAux == TargetCodePointer.Null - ? DelegateType.Closed - : DelegateType.Open, - _ => DelegateType.Unknown, - }; + IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem; + + TargetPointer mt = GetMethodTableAddress(helperObject); + Debug.Assert(mt != TargetPointer.Null); + + isMulticast = rts.IsArray(rts.GetTypeHandle(mt), out _); + } + + DelegateType delegateType = DelegateType.Unknown; + if (!isMulticast && target.ReadNInt(address + /* Delegate::ExtraData offset */) != -1) + { + delegateType = del.MethodPtrAux == TargetCodePointer.Null ? DelegateType.Closed : DelegateType.Open; + } // Pick the bound object and primary entry point based on the classification. // For Closed delegates the target is the bound `this` and MethodPtr is invoked on it. diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 6fe3cc2f661d32..b33c3c24400a8d 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -19,10 +19,11 @@ namespace System public abstract partial class Delegate : ICloneable, ISerializable { private const nint UnmanagedMarker = -1; + // This is set under 3 circumstances - // 1. Multicast delegate - // 2. Method cache - // 3. Collectible delegates + // 1. Multicast delegates - object[] + // 2. Method cache - MethodInfo + // 3. Collectible delegates - LoaderAllocator and such internal object? _helperObject; // _target is the object we will invoke on; null if static delegate @@ -39,12 +40,17 @@ public abstract partial class Delegate : ICloneable, ISerializable // to _methodPtrAux. internal nint _methodPtrAux; - // this stores the multicast count, UnmanagedMarker for unmanaged or target MethodDesc + // this stores the multicast count, UnmanagedMarker or target MethodDesc internal nint _extraData; internal bool IsUnmanagedFunctionPtr => _extraData == UnmanagedMarker; - public partial bool HasSingleTarget => _invocationList is null || _invocationList.GetType() != typeof(object[]); + public partial bool HasSingleTarget => _helperObject is null || _helperObject.GetType() != typeof(object[]); + + public object? Target => + TryGetInvocations(out ReadOnlySpan invocations) + ? invocations[^1].Target + : IsUnmanagedFunctionPtr || _methodPtrAux != 0 ? null : _target; private nuint MethodDesc { @@ -53,27 +59,8 @@ private nuint MethodDesc { Debug.Assert(HasSingleTarget); Debug.Assert(!IsUnmanagedFunctionPtr); - return _invocationCount != 0 ? (nuint)_invocationCount : GetMethodDesc(); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal bool TryGetInvocations(out ReadOnlySpan invocations) - { - if (HasSingleTarget) - { - invocations = default; - return false; + return _extraData != 0 ? (nuint)_extraData : GetMethodDesc(); } - - object[] invocationList = (object[])_invocationList!; - Debug.Assert(invocationList.Length > 1); - Debug.Assert(invocationList[0] is MulticastDelegate); - Debug.Assert((uint)invocationList.Length >= (nuint)_invocationCount); - - ref MulticastDelegate first = ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(invocationList)); - invocations = MemoryMarshal.CreateReadOnlySpan(ref first, (int)_invocationCount); - return true; } // This constructor is called from the class generated by the @@ -122,6 +109,46 @@ protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Al DelegateBindingFlags.CaselessMatching); } + // This method returns the Invocation list of this multicast delegate. + public Delegate[] GetInvocationList() => + TryGetInvocations(out ReadOnlySpan invocations) + ? ((ReadOnlySpan)invocations).ToArray() : [this]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool TryGetInvocations(out ReadOnlySpan invocations) + { + if (HasSingleTarget) + { + invocations = default; + return false; + } + + object[] invocationList = (object[])_helperObject!; + Debug.Assert(invocationList.Length > 1); + Debug.Assert(invocationList[0] is MulticastDelegate); + Debug.Assert((uint)invocationList.Length >= (nuint)_extraData); + + ref MulticastDelegate first = ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(invocationList)); + invocations = MemoryMarshal.CreateReadOnlySpan(ref first, (int)_extraData); + return true; + } + + // Used by delegate invocation list enumerator + private Delegate? TryGetAt(int index) + { + if (TryGetInvocations(out ReadOnlySpan invocations)) + { + if ((uint)index < (uint)invocations.Length) + return invocations[index]; + } + else if (index == 0) + { + return this; + } + + return null; + } + protected virtual object? DynamicInvokeImpl(object?[]? args) { RuntimeMethodHandleInternal method = new RuntimeMethodHandleInternal(GetInvokeMethod()); @@ -221,18 +248,11 @@ public override int GetHashCode() return hashCode; } - private object? GetTarget() - { - return TryGetInvocations(out ReadOnlySpan invocations) - ? invocations[^1].Target - : !IsUnmanagedFunctionPtr && _methodPtrAux == 0 ? _target : null; - } - protected virtual MethodInfo GetMethodImpl() { return TryGetInvocations(out ReadOnlySpan invocations) ? invocations[^1].Method - : _invocationList as MethodInfo ?? GetMethodImplUncached(); + : _helperObject as MethodInfo ?? GetMethodImplUncached(); [MethodImpl(MethodImplOptions.NoInlining)] MethodInfo GetMethodImplUncached() @@ -242,7 +262,7 @@ MethodInfo GetMethodImplUncached() MethodInfo method = GetMethodImplCore(); Debug.Assert(method is not null); - Volatile.Write(ref _invocationList, method); + Volatile.Write(ref _helperObject, method); return method; } } @@ -600,7 +620,7 @@ private nuint GetMethodDesc() nuint desc = GetMethodDesc(ObjectHandleOnStack.Create(ref d)); if (!IsUnmanagedFunctionPtr) { - _invocationCount = (nint)desc; + _extraData = (nint)desc; } return desc; } @@ -628,6 +648,7 @@ internal void InitializeVirtualCallStub(IntPtr methodPtr) // These flags effect the way BindToMethodInfo and BindToMethodName are allowed to bind a delegate to a target method. Their // values must be kept in sync with the definition in vm\comdelegate.h. + [Flags] internal enum DelegateBindingFlags { StaticMethodOnly = 0x00000001, // Can only bind to static target methods diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index 71a92475e4261d..c0d4c388f0a2c3 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -44,7 +44,7 @@ private static bool TrySetSlot(object?[] a, int index, object o) dd._target == d._target; } - private unsafe MulticastDelegate NewMulticastDelegate(object[] invocationList, int invocationCount, bool thisIsMultiCastAlready) + private unsafe MulticastDelegate NewMulticastDelegate(object[] invocationList, int invocationCount, bool thisIsMultiCastAlready = false) { // First, allocate a new multicast delegate just like this one, i.e. same type as the this object MulticastDelegate result = Unsafe.As(RuntimeTypeHandle.InternalAllocNoChecks(RuntimeHelpers.GetMethodTable(this))); @@ -62,17 +62,12 @@ private unsafe MulticastDelegate NewMulticastDelegate(object[] invocationList, i result._methodPtrAux = GetInvokeMethod(); } result._target = result; - result._invocationList = invocationList; - result._invocationCount = invocationCount; + result._helperObject = invocationList; + result._extraData = invocationCount; return result; } - internal MulticastDelegate NewMulticastDelegate(object[] invocationList, int invocationCount) - { - return NewMulticastDelegate(invocationList, invocationCount, false); - } - // This method will combine this delegate with the passed delegate // to form a new delegate. internal new Delegate CombineImpl(Delegate? follow) @@ -87,12 +82,12 @@ internal MulticastDelegate NewMulticastDelegate(object[] invocationList, int inv MulticastDelegate dFollow = (MulticastDelegate)follow; object[]? resultList; int followCount = 1; - object[]? followList = dFollow._invocationList as object[]; + object[]? followList = dFollow._helperObject as object[]; if (followList != null) - followCount = (int)dFollow._invocationCount; + followCount = (int)dFollow._extraData; int resultCount; - if (_invocationList is not object[] invocationList) + if (_helperObject is not object[] invocationList) { resultCount = 1 + followCount; resultList = new object[resultCount]; @@ -109,7 +104,7 @@ internal MulticastDelegate NewMulticastDelegate(object[] invocationList, int inv return NewMulticastDelegate(resultList, resultCount); } - int invocationCount = (int)_invocationCount; + int invocationCount = (int)_extraData; resultCount = invocationCount + followCount; resultList = null; if (resultCount <= invocationList.Length) @@ -161,8 +156,8 @@ internal MulticastDelegate NewMulticastDelegate(object[] invocationList, int inv private object[] DeleteFromInvocationList(object[] invocationList, int invocationCount, int deleteIndex, int deleteCount) { - Debug.Assert(_invocationList is object[]); - object[]? thisInvocationList = Unsafe.As(_invocationList); + Debug.Assert(_helperObject is object[]); + object[]? thisInvocationList = Unsafe.As(_helperObject); Debug.Assert(thisInvocationList is not null); int allocCount = thisInvocationList.Length; @@ -206,9 +201,15 @@ private static bool EqualInvocationLists(object[] a, object[] b, int start, int if (v.HasSingleTarget) { - if (_invocationList is object[] invocationList) + if (_helperObject is not object[] invocationList) + { + // they are both not real Multicast + if (Equals(value)) + return null; + } + else { - int invocationCount = (int)_invocationCount; + int invocationCount = (int)_extraData; for (int i = invocationCount; --i >= 0;) { if (v.Equals(invocationList[i])) @@ -226,41 +227,32 @@ private static bool EqualInvocationLists(object[] a, object[] b, int start, int return NewMulticastDelegate(list, invocationCount - 1, true); } } - else - { - // they are both not real Multicast - if (Equals(value)) - return null; - } } - else + else if (_helperObject is object[] invocationList) { - if (_invocationList is object[] invocationList) + int invocationCount = (int)_extraData; + int vInvocationCount = (int)v._extraData; + object[] vInvocationList = (object[])v._helperObject!; + for (int i = invocationCount - vInvocationCount; i >= 0; i--) { - int invocationCount = (int)_invocationCount; - int vInvocationCount = (int)v._invocationCount; - object[] vInvocationList = (object[])v._invocationList!; - for (int i = invocationCount - vInvocationCount; i >= 0; i--) + if (!EqualInvocationLists(invocationList, vInvocationList, i, vInvocationCount)) { - if (!EqualInvocationLists(invocationList, vInvocationList, i, vInvocationCount)) - { - continue; - } + continue; + } - switch (invocationCount - vInvocationCount) + switch (invocationCount - vInvocationCount) + { + case 0: + // Special case - no values left + return null; + case 1: + // Special case - only one value left, either at the beginning or the end + return (Delegate)invocationList[i != 0 ? 0 : invocationCount - 1]; + default: { - case 0: - // Special case - no values left - return null; - case 1: - // Special case - only one value left, either at the beginning or the end - return (Delegate)invocationList[i != 0 ? 0 : invocationCount - 1]; - default: - { - object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, - vInvocationCount); - return NewMulticastDelegate(list, invocationCount - vInvocationCount, true); - } + object[] list = DeleteFromInvocationList(invocationList, invocationCount, i, + vInvocationCount); + return NewMulticastDelegate(list, invocationCount - vInvocationCount, true); } } } @@ -269,28 +261,6 @@ private static bool EqualInvocationLists(object[] a, object[] b, int start, int return this; } - // This method returns the Invocation list of this multicast delegate. - internal new Delegate[] GetInvocationList() - { - return TryGetInvocations(out ReadOnlySpan invocations) ? ((ReadOnlySpan)invocations).ToArray() : [this]; - } - - // Used by delegate invocation list enumerator - internal Delegate? TryGetAt(int index) - { - if (TryGetInvocations(out ReadOnlySpan invocations)) - { - if ((uint)index < (uint)invocations.Length) - return invocations[index]; - } - else if (index == 0) - { - return this; - } - - return null; - } - public sealed override bool Equals([NotNullWhen(true)] object? obj) => base.Equals(obj); public sealed override int GetHashCode() => base.GetHashCode(); @@ -353,7 +323,7 @@ private void CtorCollectibleClosedStatic(object target, IntPtr methodPtr, IntPtr { _target = target; _methodPtr = (nuint)methodPtr; - _invocationList = GCHandle.InternalGet(gchandle); + _helperObject = GCHandle.InternalGet(gchandle); Debug.Assert(HasSingleTarget); } @@ -364,7 +334,7 @@ private void CtorCollectibleOpened(object target, IntPtr methodPtr, IntPtr shuff _target = this; _methodPtr = (nuint)shuffleThunk; _methodPtrAux = methodPtr; - _invocationList = GCHandle.InternalGet(gchandle); + _helperObject = GCHandle.InternalGet(gchandle); Debug.Assert(HasSingleTarget); } @@ -374,7 +344,7 @@ private void CtorCollectibleVirtualDispatch(object target, IntPtr methodPtr, Int { _target = this; _methodPtr = (nuint)shuffleThunk; - _invocationList = GCHandle.InternalGet(gchandle); + _helperObject = GCHandle.InternalGet(gchandle); Debug.Assert(HasSingleTarget); InitializeVirtualCallStub(methodPtr); } diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index e9af46740b393d..3f397ae003b73c 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -3263,22 +3263,13 @@ DacDbiInterfaceImpl::DelegateType DacDbiInterfaceImpl::GetDelegateType(VMPTR_Obj DelegateType delegateType = DelegateType::kUnknownDelegateType; PTR_DelegateObject pDelObj = dac_cast(delegateObject.GetDacPtr()); - INT_PTR invocationCount = pDelObj->GetInvocationCount(); - OBJECTREF invocationList = pDelObj->GetInvocationList(); - if (((invocationList == NULL) || !invocationList->GetMethodTable()->IsArray()) && (invocationCount != DELEGATE_MARKER_UNMANAGEDFPTR)) + INT_PTR extraData = pDelObj->GetExtraData(); + OBJECTREF helperObject = pDelObj->GetHelperObject(); + if (((helperObject == NULL) || !helperObject->GetMethodTable()->IsArray()) && (extraData != DELEGATE_MARKER_UNMANAGEDFPTR)) { - // If this delegate points to a static function or this is a open virtual delegate, this should be non-null - // This does not handle open virtual delegates correctly. + // If this delegate is open, this should be non-null TADDR targetMethodPtr = PCODEToPINSTR(pDelObj->GetMethodPtrAux()); - if (targetMethodPtr == (TADDR)NULL) - { - // Static extension methods, other closed static delegates, and instance delegates fall into this category. - delegateType = DelegateType::kClosedDelegate; - } - else - { - delegateType = DelegateType::kOpenDelegate; - } + delegateType = targetMethodPtr == (TADDR)NULL ? DelegateType::kClosedDelegate : DelegateType::kOpenDelegate; } return delegateType; } diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index 52a3fbe68d7024..57eecbb07ed79d 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -8262,7 +8262,7 @@ void DebuggerStepper::TriggerMulticastDelegate(DELEGATEREF pDel, INT32 delegateC TraceDestination trace; FramePointer fp = LEAF_MOST_FRAME; - PTRARRAYREF pDelInvocationList = (PTRARRAYREF) pDel->GetInvocationList(); + PTRARRAYREF pDelInvocationList = (PTRARRAYREF) pDel->GetHelperObject(); DELEGATEREF pCurrentInvokeDel = (DELEGATEREF) pDelInvocationList->GetAt(delegateCount); StubLinkStubManager::TraceDelegateObject((BYTE*)OBJECTREFToObject(pCurrentInvokeDel), &trace); diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index c6f5b4c8f9581e..2688816df7837f 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1287,11 +1287,11 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, (*pRefThis)->SetMethodPtr(pTargetCode); } - (*pRefThis)->SetInvocationCount((INT_PTR)pTargetMethod); + (*pRefThis)->SetExtraData((INT_PTR)pTargetMethod); LoaderAllocator *pLoaderAllocator = pTargetMethod->GetLoaderAllocator(); - _ASSERTE((*pRefThis)->GetInvocationList() == NULL); + _ASSERTE((*pRefThis)->GetHelperObject() == NULL); if (pLoaderAllocator->IsCollectible()) (*pRefThis)->SetHelperObject(pLoaderAllocator->GetExposedObject()); } @@ -1325,7 +1325,7 @@ LPVOID COMDelegate::ConvertToCallback(OBJECTREF pDelegateObj) // If we are a delegate originally created from an unmanaged function pointer, we will simply return // that function pointer. - if (DELEGATE_MARKER_UNMANAGEDFPTR == pDelegate->GetInvocationCount()) + if (DELEGATE_MARKER_UNMANAGEDFPTR == pDelegate->GetExtraData()) { pCode = pDelegate->GetMethodPtrAux(); } @@ -1510,7 +1510,7 @@ OBJECTREF COMDelegate::ConvertToDelegate(LPVOID pCallback, MethodTable* pMT) delObj->SetMethodPtrAux((PCODE)pCallback); // Also, mark this delegate as an unmanaged function pointer wrapper. - delObj->SetInvocationCount(DELEGATE_MARKER_UNMANAGEDFPTR); + delObj->SetExtraData(DELEGATE_MARKER_UNMANAGEDFPTR); } return delObj; @@ -1584,7 +1584,7 @@ extern "C" void QCALLTYPE Delegate_InitializeVirtualCallStub(QCall::ObjectHandle DELEGATEREF refThis = (DELEGATEREF)d.Get(); refThis->SetMethodPtrAux(target); - refThis->SetInvocationCount((INT_PTR)(void*)pMeth); + refThis->SetExtraData((INT_PTR)(void*)pMeth); END_QCALL; } @@ -1707,7 +1707,7 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q if (!isStatic) methodArgCount++; // count 'this' - _ASSERTE(refThis->GetInvocationList() == NULL); + _ASSERTE(refThis->GetHelperObject() == NULL); if (pMeth->GetLoaderAllocator()->IsCollectible()) refThis->SetHelperObject(pMeth->GetLoaderAllocator()->GetExposedObject()); @@ -1782,7 +1782,7 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q refThis->SetMethodPtr((PCODE)(void *)method); } - refThis->SetInvocationCount((INT_PTR)pMeth); + refThis->SetExtraData((INT_PTR)pMeth); GCPROTECT_END(); END_QCALL; @@ -1802,8 +1802,8 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) DELEGATEREF thisDel = (DELEGATEREF) orDelegate; - INT_PTR count = thisDel->GetInvocationCount(); - if (count != 0) + INT_PTR extraData = thisDel->GetExtraData(); + if (extraData != 0) { // this is one of the following: // - multicast - _invocationList is Array && _invocationCount != 0 @@ -1811,11 +1811,10 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) // - MethodDesc already cached // we return the method desc for the invoke for the first two cases - OBJECTREF invocationList = thisDel->GetInvocationList(); - if ((invocationList != NULL && invocationList->GetMethodTable()->IsArray()) || count == DELEGATE_MARKER_UNMANAGEDFPTR) + if (IsTrueMulticastDelegate(thisDel) || extraData == DELEGATE_MARKER_UNMANAGEDFPTR) return FindDelegateInvokeMethod(thisDel->GetMethodTable()); - return (MethodDesc*)count; + return (MethodDesc*)extraData; } // Next, check for an open delegate @@ -1829,30 +1828,27 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) MethodDesc *pMethodHandle = NonVirtualEntry2MethodDesc(code); _ASSERTE(pMethodHandle); - thisDel->SetInvocationCount((INT_PTR)pMethodHandle); + thisDel->SetExtraData((INT_PTR)pMethodHandle); return pMethodHandle; } -BOOL COMDelegate::IsTrueMulticastDelegate(OBJECTREF delegate) +BOOL COMDelegate::IsTrueMulticastDelegate(DELEGATEREF delegate) { CONTRACTL { - THROWS; + NOTHROW; GC_NOTRIGGER; - MODE_COOPERATIVE; + MODE_ANY; } CONTRACTL_END; - BOOL isMulticast = FALSE; - - OBJECTREF invocationList = ((DELEGATEREF)delegate)->GetInvocationList(); - if (invocationList != NULL) + OBJECTREF helperObject = delegate->GetHelperObject(); + if (helperObject == NULL) { - MethodTable *pMT = invocationList->GetMethodTable(); - isMulticast = pMT->IsArray(); + return false; } - return isMulticast; + return helperObject->GetMethodTable()->IsArray(); } // Get the cpu stub for a delegate invoke. @@ -2059,7 +2055,7 @@ extern "C" PCODE QCALLTYPE Delegate_GetMulticastInvokeSlow(MethodTable* pDelegat // Load next delegate from array using LoopCounter as index pCode->EmitLoadThis(); - pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__INVOCATION_LIST))); + pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__HELPER_OBJECT))); pCode->EmitLDLOC(dwLoopCounterNum); pCode->EmitLDELEM_REF(); @@ -2083,7 +2079,7 @@ extern "C" PCODE QCALLTYPE Delegate_GetMulticastInvokeSlow(MethodTable* pDelegat // compare LoopCounter with InvocationCount. If less then branch to nextDelegate pCode->EmitLDLOC(dwLoopCounterNum); pCode->EmitLoadThis(); - pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__INVOCATION_COUNT))); + pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__EXTRA_DATA))); pCode->EmitBLT(nextDelegate); // load the return value. return value from the last delegate call is returned diff --git a/src/coreclr/vm/comdelegate.h b/src/coreclr/vm/comdelegate.h index fe82249726fb9d..d99eaf3e16c2e0 100644 --- a/src/coreclr/vm/comdelegate.h +++ b/src/coreclr/vm/comdelegate.h @@ -60,7 +60,7 @@ class COMDelegate static MethodDesc * __fastcall GetMethodDesc(OBJECTREF obj); - static BOOL IsTrueMulticastDelegate(OBJECTREF delegate); + static BOOL IsTrueMulticastDelegate(DELEGATEREF delegate); // Throw if the method violates any usage restrictions // for UnmanagedCallersOnlyAttribute. diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index a9e4ae488bcfa8..bf52b11099f3a4 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -227,20 +227,20 @@ DEFINE_METHOD(DATE_TIME, LONG_CTOR, .ctor, DEFINE_CLASS(DECIMAL, System, Decimal) DEFINE_METHOD(DECIMAL, CURRENCY_CTOR, .ctor, IM_Currency_RetVoid) -DEFINE_CLASS_U(System, Delegate, NoClass) -DEFINE_FIELD_U(_helperObject, DelegateObject, _helperObject) -DEFINE_FIELD_U(_target, DelegateObject, _target) -DEFINE_FIELD_U(_methodPtr, DelegateObject, _methodPtr) -DEFINE_FIELD_U(_methodPtrAux, DelegateObject, _methodPtrAux) -DEFINE_FIELD_U(_invocationCount, DelegateObject, _invocationCount) -DEFINE_CLASS(DELEGATE, System, Delegate) -DEFINE_FIELD(DELEGATE, HELPER_OBJECT, _helperObject) -DEFINE_FIELD(DELEGATE, TARGET, _target) -DEFINE_FIELD(DELEGATE, METHOD_PTR, _methodPtr) -DEFINE_FIELD(DELEGATE, METHOD_PTR_AUX, _methodPtrAux) -DEFINE_FIELD(DELEGATE, INVOCATION_COUNT, _invocationCount) -DEFINE_METHOD(DELEGATE, CONSTRUCT_DELEGATE, DelegateConstruct, IM_Obj_IntPtr_RetVoid) -DEFINE_METHOD(DELEGATE, GET_INVOKE_METHOD, GetInvokeMethod, IM_RetIntPtr) +DEFINE_CLASS_U(System, Delegate, NoClass) +DEFINE_FIELD_U(_helperObject, DelegateObject, _helperObject) +DEFINE_FIELD_U(_target, DelegateObject, _target) +DEFINE_FIELD_U(_methodPtr, DelegateObject, _methodPtr) +DEFINE_FIELD_U(_methodPtrAux, DelegateObject, _methodPtrAux) +DEFINE_FIELD_U(_extraData, DelegateObject, _extraData) +DEFINE_CLASS(DELEGATE, System, Delegate) +DEFINE_FIELD(DELEGATE, HELPER_OBJECT, _helperObject) +DEFINE_FIELD(DELEGATE, TARGET, _target) +DEFINE_FIELD(DELEGATE, METHOD_PTR, _methodPtr) +DEFINE_FIELD(DELEGATE, METHOD_PTR_AUX, _methodPtrAux) +DEFINE_FIELD(DELEGATE, EXTRA_DATA, _extraData) +DEFINE_METHOD(DELEGATE, CONSTRUCT_DELEGATE, DelegateConstruct, IM_Obj_IntPtr_RetVoid) +DEFINE_METHOD(DELEGATE, GET_INVOKE_METHOD, GetInvokeMethod, IM_RetIntPtr) DEFINE_CLASS(INT128, System, Int128) DEFINE_CLASS(UINT128, System, UInt128) diff --git a/src/coreclr/vm/datadescriptor/datadescriptor.inc b/src/coreclr/vm/datadescriptor/datadescriptor.inc index 065f950ab7b3c5..f150b85ec07b8c 100644 --- a/src/coreclr/vm/datadescriptor/datadescriptor.inc +++ b/src/coreclr/vm/datadescriptor/datadescriptor.inc @@ -212,10 +212,11 @@ CDAC_TYPE_END(Array) CDAC_TYPE_BEGIN(Delegate) CDAC_TYPE_SIZE(sizeof(DelegateObject)) +CDAC_TYPE_FIELD(Delegate, T_POINTER, HelperObject, cdac_data::HelperObject) CDAC_TYPE_FIELD(Delegate, T_POINTER, Target, cdac_data::Target) CDAC_TYPE_FIELD(Delegate, T_POINTER, MethodPtr, cdac_data::MethodPtr) CDAC_TYPE_FIELD(Delegate, T_POINTER, MethodPtrAux, cdac_data::MethodPtrAux) -CDAC_TYPE_FIELD(Delegate, T_NINT, InvocationCount, cdac_data::InvocationCount) +CDAC_TYPE_FIELD(Delegate, T_NINT, ExtraData, cdac_data::ExtraData) CDAC_TYPE_END(Delegate) CDAC_TYPE_BEGIN(TypedByRef) diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 88e1f4e7efe06b..e8c9cf7e4420f4 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -3384,7 +3384,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr if (isOpenVirtual) { - targetMethod = (MethodDesc*)(*delegateObj)->GetInvocationCount(); + targetMethod = COMDelegate::GetMethodDesc(*delegateObj); OBJECTREF *pThisArg = LOCAL_VAR_ADDR(callArgsOffset + INTERP_STACK_SLOT_SIZE, OBJECTREF); NULL_CHECK(*pThisArg); targetMethod = CallWithSEHWrapper( diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 10b76b3a62ef26..1ccdcdecdf7f95 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1907,13 +1907,12 @@ HCIMPL2(void, JIT_DelegateProfile32, Object *obj, ICorJitInfo::HandleHistogram32 // We filter out multicast and unmanaged here. DELEGATEREF del = (DELEGATEREF)objRef; - INT_PTR invocationCount = del->GetInvocationCount(); - OBJECTREF invocationList = del->GetInvocationList(); + INT_PTR extraData = del->GetExtraData(); MethodDesc* pRecordedMD = (MethodDesc*)DEFAULT_UNKNOWN_HANDLE; - if (((invocationList == NULL) || !invocationList->GetMethodTable()->IsArray()) && (invocationCount != DELEGATE_MARKER_UNMANAGEDFPTR)) + if (!COMDelegate::IsTrueMulticastDelegate(del) && (extraData != DELEGATE_MARKER_UNMANAGEDFPTR)) { - MethodDesc* pMD = (MethodDesc*)invocationCount; + MethodDesc* pMD = (MethodDesc*)extraData; if ((pMD == nullptr) && (del->GetMethodPtrAux() == NULL)) { pMD = NonVirtualEntry2MethodDesc(del->GetMethodPtr()); @@ -1959,13 +1958,12 @@ HCIMPL2(void, JIT_DelegateProfile64, Object *obj, ICorJitInfo::HandleHistogram64 // We filter out multicast and unmanaged here. DELEGATEREF del = (DELEGATEREF)objRef; - INT_PTR invocationCount = del->GetInvocationCount(); - OBJECTREF invocationList = del->GetInvocationList(); + INT_PTR extraData = del->GetExtraData(); MethodDesc* pRecordedMD = (MethodDesc*)DEFAULT_UNKNOWN_HANDLE; - if (((invocationList == NULL) || !invocationList->GetMethodTable()->IsArray()) && (invocationCount != DELEGATE_MARKER_UNMANAGEDFPTR)) + if (!COMDelegate::IsTrueMulticastDelegate(del) && (extraData != DELEGATE_MARKER_UNMANAGEDFPTR)) { - MethodDesc* pMD = (MethodDesc*)invocationCount; + MethodDesc* pMD = (MethodDesc*)extraData; if ((pMD == nullptr) && (del->GetMethodPtrAux() == NULL)) { pMD = NonVirtualEntry2MethodDesc(del->GetMethodPtr()); diff --git a/src/coreclr/vm/object.h b/src/coreclr/vm/object.h index a042b67008d400..a9710bb168cf94 100644 --- a/src/coreclr/vm/object.h +++ b/src/coreclr/vm/object.h @@ -1753,9 +1753,9 @@ class DelegateObject : public Object friend struct ::cdac_data; public: - OBJECTREF GetInvocationList() { LIMITED_METHOD_CONTRACT; return _invocationList; } - void SetInvocationList(OBJECTREF invocationList) { WRAPPER_NO_CONTRACT; SetObjectReference(&_invocationList, invocationList); } - static int GetOffsetOfInvocationList() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _invocationList); } + OBJECTREF GetHelperObject() { LIMITED_METHOD_CONTRACT; return _helperObject; } + void SetHelperObject(OBJECTREF helperObject) { WRAPPER_NO_CONTRACT; SetObjectReference(&_helperObject, helperObject); } + static int GetOffsetOfHelperObject() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _helperObject); } OBJECTREF GetTarget() { LIMITED_METHOD_CONTRACT; return _target; } void SetTarget(OBJECTREF target) { WRAPPER_NO_CONTRACT; SetObjectReference(&_target, target); } @@ -1769,9 +1769,9 @@ class DelegateObject : public Object void SetMethodPtrAux(PCODE methodPtrAux) { LIMITED_METHOD_CONTRACT; _methodPtrAux = methodPtrAux; } static int GetOffsetOfMethodPtrAux() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _methodPtrAux); } - INT_PTR GetInvocationCount() { LIMITED_METHOD_CONTRACT; return _invocationCount; } - void SetInvocationCount(INT_PTR invocationCount) { LIMITED_METHOD_CONTRACT; _invocationCount = invocationCount; } - static int GetOffsetOfInvocationCount() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _invocationCount); } + INT_PTR GetExtraData() { LIMITED_METHOD_CONTRACT; return _extraData; } + void SetExtraData(INT_PTR extraData) { LIMITED_METHOD_CONTRACT; _extraData = extraData; } + static int GetOffsetOfExtraData() { LIMITED_METHOD_CONTRACT; return offsetof(DelegateObject, _extraData); } // README: // If you modify the order of these fields, make sure to update the definition in @@ -1781,7 +1781,7 @@ class DelegateObject : public Object OBJECTREF _target; PCODE _methodPtr; PCODE _methodPtrAux; - INT_PTR _invocationCount; + INT_PTR _extraData; }; #define OFFSETOF__DelegateObject__target (OBJECT_SIZE /* m_pMethTab */ + TARGET_POINTER_SIZE /* _helperObject */) @@ -1791,10 +1791,11 @@ class DelegateObject : public Object template<> struct cdac_data { + static constexpr size_t HelperObject = offsetof(DelegateObject, _helperObject); static constexpr size_t Target = offsetof(DelegateObject, _target); static constexpr size_t MethodPtr = offsetof(DelegateObject, _methodPtr); static constexpr size_t MethodPtrAux = offsetof(DelegateObject, _methodPtrAux); - static constexpr size_t InvocationCount = offsetof(DelegateObject, _invocationCount); + static constexpr size_t ExtraData = offsetof(DelegateObject, _extraData); }; #ifdef USE_CHECKED_OBJECTREFS diff --git a/src/coreclr/vm/stubmgr.cpp b/src/coreclr/vm/stubmgr.cpp index 3d79f096d71d36..c6479ccfd517ec 100644 --- a/src/coreclr/vm/stubmgr.cpp +++ b/src/coreclr/vm/stubmgr.cpp @@ -1208,7 +1208,7 @@ BOOL StubLinkStubManager::TraceDelegateObject(BYTE* pbDel, TraceDestination *tra // If we got here, then we're here b/c we're at the start of a delegate stub // need to figure out the kind of delegates we are dealing with. - BYTE *pbDelInvocationList = *(BYTE **)(pbDel + DelegateObject::GetOffsetOfInvocationList()); + BYTE *pbDelInvocationList = *(BYTE **)(pbDel + DelegateObject::GetOffsetOfHelperObject()); LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: invocationList: %p\n", pbDelInvocationList)); @@ -1239,9 +1239,9 @@ BOOL StubLinkStubManager::TraceDelegateObject(BYTE* pbDel, TraceDestination *tra LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: ppbDest: %p *ppbDest:%p\n", ppbDest, *ppbDest)); - BOOL res = StubManager::TraceStub((PCODE) (*ppbDest), trace); + BOOL res = StubManager::TraceStub((PCODE) *ppbDest, trace); - LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: res: %s, result type: %d\n", (res ? "true" : "false"), trace->GetTraceType())); + LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: res: %s, result type: %d\n", res ? "true" : "false", trace->GetTraceType())); return res; } @@ -1250,7 +1250,7 @@ BOOL StubLinkStubManager::TraceDelegateObject(BYTE* pbDel, TraceDestination *tra // In order to go to the correct spot, we have just have to fish out // slot 0 of the invocation list, and figure out where that's going to, // then put a breakpoint there. - pbDel = *(BYTE**)(((ArrayBase *)pbDelInvocationList)->GetDataPtr()); + pbDel = *(BYTE**)((ArrayBase *)pbDelInvocationList)->GetDataPtr(); return TraceDelegateObject(pbDel, trace); } diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index e3faa10a51b1d8..5a145f51bd8ad8 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -1475,7 +1475,7 @@ extern "C" PCODE CID_VirtualOpenDelegateDispatchWorker(TransitionBlock * pTransi _ASSERTE(!"Throw returned"); } - MethodDesc *pTargetMD = (MethodDesc*)((DELEGATEREF)delegateObj)->GetInvocationCount(); + MethodDesc *pTargetMD = COMDelegate::GetMethodDesc((DELEGATEREF)delegateObj); pSDFrame->SetFunction(pTargetMD); pSDFrame->Push(CURRENT_THREAD); diff --git a/src/libraries/System.Private.CoreLib/src/System/Delegate.cs b/src/libraries/System.Private.CoreLib/src/System/Delegate.cs index 1fddcf0357ba25..7557aa4a8598e1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Delegate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Delegate.cs @@ -72,15 +72,11 @@ public abstract partial class Delegate : ICloneable, ISerializable protected Delegate? RemoveImpl(Delegate? d) => Unsafe.As(this).RemoveImpl(d); - public Delegate[] GetInvocationList() => Unsafe.As(this).GetInvocationList(); - /// /// Gets a value that indicates whether the has a single invocation target. /// /// true if the has a single invocation target. - public bool HasSingleTarget => Unsafe.As(this).HasSingleTarget; - - public object? Target => Unsafe.As(this).Target; + public partial bool HasSingleTarget { get; } #endif /// diff --git a/src/mono/System.Private.CoreLib/src/System/Delegate.Mono.cs b/src/mono/System.Private.CoreLib/src/System/Delegate.Mono.cs index fa749f1e9a6b64..849ac3c54a1796 100644 --- a/src/mono/System.Private.CoreLib/src/System/Delegate.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Delegate.Mono.cs @@ -105,8 +105,14 @@ protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Al }; } + public partial bool HasSingleTarget => Unsafe.As(this).HasSingleTarget; + + public object? Target => Unsafe.As(this).Target; + internal virtual object? GetTarget() => _target; + public Delegate[] GetInvocationList() => Unsafe.As(this).GetInvocationList(); + public static Delegate CreateDelegate(Type type, object? firstArgument, MethodInfo method, bool throwOnBindFailure) { return CreateDelegate(type, firstArgument, method, throwOnBindFailure, true)!; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Object_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Object_1.cs index 6607f78295f1d5..031fbc3c8d180f 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Object_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Object_1.cs @@ -163,15 +163,22 @@ public DelegateInfo GetDelegateInfo(TargetPointer address) { Data.Delegate del = _target.ProcessedData.GetOrAdd(address); - // Classify by invocation count first to handle multicast and unmanaged. - // This does not handle open virtual delegates correctly. + // Check for multicast and unmanaged first. + bool isMulticast = false; + if (del.HelperObject != TargetPointer.Null) + { + IRuntimeTypeSystem typeSystemContract = _target.Contracts.RuntimeTypeSystem; + + TargetPointer mt = GetMethodTableAddress(del.HelperObject); + Debug.Assert(mt != TargetPointer.Null); + + isMulticast = typeSystemContract.IsArray(typeSystemContract.GetTypeHandle(mt), out _); + } + DelegateType delegateType = DelegateType.Unknown; - if (del.InvocationCount.Value == 0) + if (!isMulticast && del.ExtraData.Value != Data.Delegate.UnmanagedMarker) { - if (del.MethodPtrAux == TargetCodePointer.Null) - delegateType = DelegateType.Closed; - else - delegateType = DelegateType.Open; + delegateType = del.MethodPtrAux == TargetCodePointer.Null ? DelegateType.Closed : DelegateType.Open; } (TargetPointer targetObject, TargetCodePointer targetMethodPtr) = delegateType switch diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Delegate.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Delegate.cs index 1b87c4beb4db0a..48fe0051ae57d0 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Delegate.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Delegate.cs @@ -6,8 +6,11 @@ namespace Microsoft.Diagnostics.DataContractReader.Data; [CdacType(nameof(DataType.Delegate))] internal sealed partial class Delegate : IData { + public const long UnmanagedMarker = -1; + + [Field] public TargetPointer HelperObject { get; } [Field] public TargetPointer Target { get; } [Field] public TargetCodePointer MethodPtr { get; } [Field] public TargetCodePointer MethodPtrAux { get; } - [Field] public TargetNInt InvocationCount { get; } + [Field] public TargetNInt ExtraData { get; } } From cab6bf2268414ba6bfa945427bcb6162a72e3b7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Mon, 29 Jun 2026 01:50:34 +0200 Subject: [PATCH 46/77] Cleanup merge --- .../src/System/MulticastDelegate.CoreCLR.cs | 2 +- src/coreclr/vm/comdelegate.cpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index c0d4c388f0a2c3..9d792c849cdf5a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -204,7 +204,7 @@ private static bool EqualInvocationLists(object[] a, object[] b, int start, int if (_helperObject is not object[] invocationList) { // they are both not real Multicast - if (Equals(value)) + if (Equals(v)) return null; } else diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 2688816df7837f..7846b4fda545ea 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -2601,14 +2601,14 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT // DELEGATE KINDS TABLE // - // _invocationList _target _methodPtr _methodPtrAux + // _target _methodPtr _methodPtrAux // - // 1- Instance closed null 'this' ptr target method null - // 2- Instance open non-virt null delegate shuffle thunk target method - // 3- Instance open virtual null delegate Virtual-stub dispatch method id - // 4- Static closed null first arg target method null - // 5- Static closed (special sig) null first arg ThisPtrRetBuf precode null - // 6- Static opened null delegate shuffle thunk target method + // 1- Instance closed 'this' ptr target method null + // 2- Instance open non-virt delegate shuffle thunk target method + // 3- Instance open virtual delegate Virtual-stub dispatch method id + // 4- Static closed first arg target method null + // 5- Static closed (retbuf) first arg ThisPtrRetBuf precode null + // 6- Static open delegate shuffle thunk target method // // Delegate invoke arg count == target method arg count - 2, 3, 6 // Delegate invoke arg count == 1 + target method arg count - 1, 4, 5 From 4d731daa44da65fbb1fca976f37851f5e855e212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 29 Jun 2026 01:52:52 +0200 Subject: [PATCH 47/77] Update comment for virtual delegate method id --- src/coreclr/vm/comdelegate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 7846b4fda545ea..35308b3732cacb 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -2605,7 +2605,7 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT // // 1- Instance closed 'this' ptr target method null // 2- Instance open non-virt delegate shuffle thunk target method - // 3- Instance open virtual delegate Virtual-stub dispatch method id + // 3- Instance open virtual delegate Virtual call stub method id // 4- Static closed first arg target method null // 5- Static closed (retbuf) first arg ThisPtrRetBuf precode null // 6- Static open delegate shuffle thunk target method From d99b9611b4ddd226996e822737da233742830fd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 29 Jun 2026 02:01:47 +0200 Subject: [PATCH 48/77] Update comment for virtual delegate handling --- src/coreclr/vm/comdelegate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 35308b3732cacb..9d3dcc65b5ed5b 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -2605,7 +2605,7 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT // // 1- Instance closed 'this' ptr target method null // 2- Instance open non-virt delegate shuffle thunk target method - // 3- Instance open virtual delegate Virtual call stub method id + // 3- Instance open virtual delegate shuffle thunk Virtual call stub // 4- Static closed first arg target method null // 5- Static closed (retbuf) first arg ThisPtrRetBuf precode null // 6- Static open delegate shuffle thunk target method From b2250c7ab6c2b4186fc6c9dc771553b55a8ff6e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Mon, 29 Jun 2026 02:52:20 +0200 Subject: [PATCH 49/77] Review feedback --- .../src/System/Delegate.CoreCLR.cs | 38 +++++++++---------- .../src/System/MulticastDelegate.CoreCLR.cs | 16 ++++---- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index b33c3c24400a8d..cea3173f76f2c3 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -32,18 +32,18 @@ public abstract partial class Delegate : ICloneable, ISerializable // _methodPtr is a pointer to the method we will invoke // It could be a small thunk if this is a static or UM call - internal nuint _methodPtr; + internal IntPtr _methodPtr; // In the case of a static method passed to a delegate, this field stores // whatever _methodPtr would have stored: and _methodPtr points to a // small thunk which removes the "this" pointer before going on // to _methodPtrAux. - internal nint _methodPtrAux; + internal IntPtr _methodPtrAux; // this stores the multicast count, UnmanagedMarker or target MethodDesc internal nint _extraData; - internal bool IsUnmanagedFunctionPtr => _extraData == UnmanagedMarker; + private bool IsUnmanagedFunctionPtr => _extraData == UnmanagedMarker; public partial bool HasSingleTarget => _helperObject is null || _helperObject.GetType() != typeof(object[]); @@ -52,14 +52,14 @@ public abstract partial class Delegate : ICloneable, ISerializable ? invocations[^1].Target : IsUnmanagedFunctionPtr || _methodPtrAux != 0 ? null : _target; - private nuint MethodDesc + private unsafe MethodDesc* MethodDesc { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { Debug.Assert(HasSingleTarget); Debug.Assert(!IsUnmanagedFunctionPtr); - return _extraData != 0 ? (nuint)_extraData : GetMethodDesc(); + return _extraData != 0 ? (MethodDesc*)_extraData : GetMethodDesc(); } } @@ -159,7 +159,7 @@ private bool TryGetInvocations(out ReadOnlySpan invocations) // equals returns true IIF the delegate is not null and has the // same target, method and invocation list as this object - public override bool Equals([NotNullWhen(true)] object? obj) + public override unsafe bool Equals([NotNullWhen(true)] object? obj) { if (obj == null) return false; @@ -223,7 +223,7 @@ public override bool Equals([NotNullWhen(true)] object? obj) return MethodDesc == other.MethodDesc; } - public override int GetHashCode() + public override unsafe int GetHashCode() { if (TryGetInvocations(out ReadOnlySpan invocations)) { @@ -240,7 +240,7 @@ public override int GetHashCode() return HashCode.Combine(_methodPtr, _methodPtr); } - int hashCode = MethodDesc.GetHashCode(); + int hashCode = ((nuint)MethodDesc).GetHashCode(); if (_methodPtrAux == 0 && _target != null) { hashCode += RuntimeHelpers.GetHashCode(_target) * 33; @@ -267,7 +267,7 @@ MethodInfo GetMethodImplUncached() } } - private MethodInfo GetMethodImplCore() + private unsafe MethodInfo GetMethodImplCore() { // should be handled by GetMethodImpl Debug.Assert(HasSingleTarget); @@ -578,7 +578,7 @@ private void DelegateConstruct(object target, IntPtr method) [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_GetMulticastInvokeSlow")] private static unsafe partial void* GetMulticastInvokeSlow(MethodTable* pMT); - internal unsafe nuint GetMulticastInvoke() + internal unsafe IntPtr GetMulticastInvoke() { MethodTable* pMT = RuntimeHelpers.GetMethodTable(this); void* ptr = GetMulticastInvoke(pMT); @@ -589,7 +589,7 @@ internal unsafe nuint GetMulticastInvoke() Debug.Assert(ptr == GetMulticastInvoke(pMT)); } // No GC.KeepAlive() since the caller must keep instance alive to use returned pointer. - return (nuint)ptr; + return (IntPtr)ptr; } [MethodImpl(MethodImplOptions.InternalCall)] @@ -603,7 +603,7 @@ internal unsafe IntPtr GetInvokeMethod() return (IntPtr)ptr; } - internal static IRuntimeMethodInfo CreateMethodInfo(nuint methodDesc) + internal static unsafe IRuntimeMethodInfo CreateMethodInfo(MethodDesc* methodDesc) { IRuntimeMethodInfo? methodInfo = null; CreateMethodInfo(methodDesc, ObjectHandleOnStack.Create(ref methodInfo)); @@ -611,22 +611,22 @@ internal static IRuntimeMethodInfo CreateMethodInfo(nuint methodDesc) } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_CreateMethodInfo")] - private static partial void CreateMethodInfo(nuint methodDesc, ObjectHandleOnStack retMethodInfo); + private static unsafe partial void CreateMethodInfo(MethodDesc* methodDesc, ObjectHandleOnStack retMethodInfo); [MethodImpl(MethodImplOptions.NoInlining)] - private nuint GetMethodDesc() + private unsafe MethodDesc* GetMethodDesc() { - Delegate d = this; - nuint desc = GetMethodDesc(ObjectHandleOnStack.Create(ref d)); + Delegate instance = this; + MethodDesc* methodDesc = GetMethodDesc(ObjectHandleOnStack.Create(ref instance)); if (!IsUnmanagedFunctionPtr) { - _extraData = (nint)desc; + _extraData = (nint)methodDesc; } - return desc; + return methodDesc; } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_GetMethodDesc")] - private static partial nuint GetMethodDesc(ObjectHandleOnStack instance); + private static unsafe partial MethodDesc* GetMethodDesc(ObjectHandleOnStack instance); internal static IntPtr AdjustTarget(object target, IntPtr methodPtr) { diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index 9d792c849cdf5a..34f33925cf90cf 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -278,7 +278,7 @@ private void CtorClosed(object target, IntPtr methodPtr) if (target == null) ThrowNullThisInDelegateToInstance(); _target = target; - _methodPtr = (nuint)methodPtr; + _methodPtr = methodPtr; } [DebuggerNonUserCode] @@ -286,7 +286,7 @@ private void CtorClosed(object target, IntPtr methodPtr) private void CtorClosedStatic(object target, IntPtr methodPtr) { _target = target; - _methodPtr = (nuint)methodPtr; + _methodPtr = methodPtr; } [DebuggerNonUserCode] @@ -296,7 +296,7 @@ private void CtorRTClosed(object target, IntPtr methodPtr) if (target == null) ThrowNullThisInDelegateToInstance(); _target = target; - _methodPtr = (nuint)AdjustTarget(target, methodPtr); + _methodPtr = AdjustTarget(target, methodPtr); } [DebuggerNonUserCode] @@ -304,7 +304,7 @@ private void CtorRTClosed(object target, IntPtr methodPtr) private void CtorOpened(object target, IntPtr methodPtr, IntPtr shuffleThunk) { _target = this; - _methodPtr = (nuint)shuffleThunk; + _methodPtr = shuffleThunk; _methodPtrAux = methodPtr; } @@ -313,7 +313,7 @@ private void CtorOpened(object target, IntPtr methodPtr, IntPtr shuffleThunk) private void CtorVirtualDispatch(object target, IntPtr methodPtr, IntPtr shuffleThunk) { _target = this; - _methodPtr = (nuint)shuffleThunk; + _methodPtr = shuffleThunk; InitializeVirtualCallStub(methodPtr); } @@ -322,7 +322,7 @@ private void CtorVirtualDispatch(object target, IntPtr methodPtr, IntPtr shuffle private void CtorCollectibleClosedStatic(object target, IntPtr methodPtr, IntPtr gchandle) { _target = target; - _methodPtr = (nuint)methodPtr; + _methodPtr = methodPtr; _helperObject = GCHandle.InternalGet(gchandle); Debug.Assert(HasSingleTarget); } @@ -332,7 +332,7 @@ private void CtorCollectibleClosedStatic(object target, IntPtr methodPtr, IntPtr private void CtorCollectibleOpened(object target, IntPtr methodPtr, IntPtr shuffleThunk, IntPtr gchandle) { _target = this; - _methodPtr = (nuint)shuffleThunk; + _methodPtr = shuffleThunk; _methodPtrAux = methodPtr; _helperObject = GCHandle.InternalGet(gchandle); Debug.Assert(HasSingleTarget); @@ -343,7 +343,7 @@ private void CtorCollectibleOpened(object target, IntPtr methodPtr, IntPtr shuff private void CtorCollectibleVirtualDispatch(object target, IntPtr methodPtr, IntPtr shuffleThunk, IntPtr gchandle) { _target = this; - _methodPtr = (nuint)shuffleThunk; + _methodPtr = shuffleThunk; _helperObject = GCHandle.InternalGet(gchandle); Debug.Assert(HasSingleTarget); InitializeVirtualCallStub(methodPtr); From cdda5794d49a6697c36cb21bfddf700b42fcbf30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 29 Jun 2026 03:20:55 +0200 Subject: [PATCH 50/77] Cast NULL to PCODE in comdelegate.cpp --- src/coreclr/vm/comdelegate.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 9d3dcc65b5ed5b..b27806f2051172 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1257,7 +1257,7 @@ void COMDelegate::BindToMethod(DELEGATEREF *pRefThis, } else { - PCODE pTargetCode = NULL; + PCODE pTargetCode = (PCODE)NULL; // For virtual methods we can (and should) virtualize the call now (so we don't have to insert a thunk to do so at runtime). // @@ -1819,7 +1819,7 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) // Next, check for an open delegate PCODE code = thisDel->GetMethodPtrAux(); - if (code == NULL) + if (code == (PCODE)NULL) { // Must be a closed delegate code = thisDel->GetMethodPtr(); From 8104da0ffb8efd94d6a9813c022125fdc26915d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 29 Jun 2026 03:45:54 +0200 Subject: [PATCH 51/77] Fix null pointer comparison in jithelpers.cpp --- src/coreclr/vm/jithelpers.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index 1ccdcdecdf7f95..a98420662be310 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1913,7 +1913,7 @@ HCIMPL2(void, JIT_DelegateProfile32, Object *obj, ICorJitInfo::HandleHistogram32 if (!COMDelegate::IsTrueMulticastDelegate(del) && (extraData != DELEGATE_MARKER_UNMANAGEDFPTR)) { MethodDesc* pMD = (MethodDesc*)extraData; - if ((pMD == nullptr) && (del->GetMethodPtrAux() == NULL)) + if ((pMD == nullptr) && (del->GetMethodPtrAux() == (PCODE)NULL)) { pMD = NonVirtualEntry2MethodDesc(del->GetMethodPtr()); } @@ -1964,7 +1964,7 @@ HCIMPL2(void, JIT_DelegateProfile64, Object *obj, ICorJitInfo::HandleHistogram64 if (!COMDelegate::IsTrueMulticastDelegate(del) && (extraData != DELEGATE_MARKER_UNMANAGEDFPTR)) { MethodDesc* pMD = (MethodDesc*)extraData; - if ((pMD == nullptr) && (del->GetMethodPtrAux() == NULL)) + if ((pMD == nullptr) && (del->GetMethodPtrAux() == (PCODE)NULL)) { pMD = NonVirtualEntry2MethodDesc(del->GetMethodPtr()); } From 3732a37156df45afca8a5b01e443b3488fe35403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 29 Jun 2026 04:46:18 +0200 Subject: [PATCH 52/77] Fix method descriptor retrieval for delegate object --- src/coreclr/vm/virtualcallstub.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index 5a145f51bd8ad8..e3faa10a51b1d8 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -1475,7 +1475,7 @@ extern "C" PCODE CID_VirtualOpenDelegateDispatchWorker(TransitionBlock * pTransi _ASSERTE(!"Throw returned"); } - MethodDesc *pTargetMD = COMDelegate::GetMethodDesc((DELEGATEREF)delegateObj); + MethodDesc *pTargetMD = (MethodDesc*)((DELEGATEREF)delegateObj)->GetInvocationCount(); pSDFrame->SetFunction(pTargetMD); pSDFrame->Push(CURRENT_THREAD); From 42253f8d0335d912602fb22878b9cc715c7491d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 29 Jun 2026 04:46:44 +0200 Subject: [PATCH 53/77] Change delegate invocation count retrieval to extra data --- src/coreclr/vm/virtualcallstub.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index e3faa10a51b1d8..117cc741deff74 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -1475,7 +1475,7 @@ extern "C" PCODE CID_VirtualOpenDelegateDispatchWorker(TransitionBlock * pTransi _ASSERTE(!"Throw returned"); } - MethodDesc *pTargetMD = (MethodDesc*)((DELEGATEREF)delegateObj)->GetInvocationCount(); + MethodDesc *pTargetMD = (MethodDesc*)((DELEGATEREF)delegateObj)->GetExtraData(); pSDFrame->SetFunction(pTargetMD); pSDFrame->Push(CURRENT_THREAD); From 527853e49e1175f5cb277eb6e84165162a8fa627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 29 Jun 2026 05:36:42 +0200 Subject: [PATCH 54/77] Add HelperObject and ExtraData fields to MockDelegateObjectData --- .../MockDescriptors/MockDescriptors.Object.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.Object.cs b/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.Object.cs index e92b5d34fb22bf..d69b6ed7037c6f 100644 --- a/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.Object.cs +++ b/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.Object.cs @@ -144,20 +144,22 @@ public ulong Object internal sealed class MockDelegateObjectData : TypedView { public const string MethodTableFieldName = "m_pMethTab"; + public const string HelperObjectFieldName = "HelperObject"; public const string TargetFieldName = "Target"; public const string MethodPtrFieldName = "MethodPtr"; public const string MethodPtrAuxFieldName = "MethodPtrAux"; - public const string InvocationCountFieldName = "InvocationCount"; + public const string ExtraDataFieldName = "ExtraData"; public static Layout CreateLayout(MockTarget.Architecture architecture) { SequentialLayoutBuilder builder = new("Delegate", architecture); return builder .AddPointerField(MethodTableFieldName) + .AddPointerField(HelperObjectFieldName) .AddPointerField(TargetFieldName) .AddPointerField(MethodPtrFieldName) .AddPointerField(MethodPtrAuxFieldName) - .AddNIntField(InvocationCountFieldName) + .AddNIntField(ExtraDataFieldName) .Build(); } @@ -167,6 +169,12 @@ public ulong MethodTable set => WritePointerField(MethodTableFieldName, value); } + public ulong HelperObject + { + get => ReadPointerField(HelperObjectFieldName); + set => WritePointerField(HelperObjectFieldName, value); + } + public ulong Target { get => ReadPointerField(TargetFieldName); @@ -185,12 +193,12 @@ public ulong MethodPtrAux set => WritePointerField(MethodPtrAuxFieldName, value); } - public long InvocationCount + public long ExtraData { // Stored at pointer width; on 32-bit, WritePointer truncates the upper bits // so the signed bit pattern of `value` is preserved (e.g. -1 → 0xFFFFFFFF). - get => unchecked((long)ReadPointerField(InvocationCountFieldName)); - set => WritePointerField(InvocationCountFieldName, unchecked((ulong)value)); + get => unchecked((long)ReadPointerField(ExtraDataFieldName)); + set => WritePointerField(ExtraDataFieldName, unchecked((ulong)value)); } } From 137e629ab5cf9418ef42958ae07954cfcdf7df39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 29 Jun 2026 05:50:17 +0200 Subject: [PATCH 55/77] Rename invocationCount to extraData in AddDelegateObject --- .../tests/UnitTests/MockDescriptors/MockDescriptors.Object.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.Object.cs b/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.Object.cs index d69b6ed7037c6f..28f227e4f336b5 100644 --- a/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.Object.cs +++ b/src/native/managed/cdac/tests/UnitTests/MockDescriptors/MockDescriptors.Object.cs @@ -420,7 +420,7 @@ internal ulong AddArrayObject(Array array) return fragment.Address; } - internal ulong AddDelegateObject(ulong methodTable, ulong target, ulong methodPtr, ulong methodPtrAux, long invocationCount) + internal ulong AddDelegateObject(ulong methodTable, ulong target, ulong methodPtr, ulong methodPtrAux, long extraData) { MockMemorySpace.HeapFragment fragment = ManagedObjectAllocator.Allocate((uint)DelegateLayout.Size, $"Delegate : MT = '{methodTable}'"); MockDelegateObjectData mockDelegate = DelegateLayout.Create(fragment); @@ -428,7 +428,7 @@ internal ulong AddDelegateObject(ulong methodTable, ulong target, ulong methodPt mockDelegate.Target = target; mockDelegate.MethodPtr = methodPtr; mockDelegate.MethodPtrAux = methodPtrAux; - mockDelegate.InvocationCount = invocationCount; + mockDelegate.ExtraData = extraData; return fragment.Address; } From 721607b12b94d08f1cd56f3a8b03bbfc5874e75b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 29 Jun 2026 06:02:57 +0200 Subject: [PATCH 56/77] Refactor GetDelegateInfo test method parameters --- src/native/managed/cdac/tests/UnitTests/ObjectTests.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/native/managed/cdac/tests/UnitTests/ObjectTests.cs b/src/native/managed/cdac/tests/UnitTests/ObjectTests.cs index 8e5d367eebdabd..aec9e6e73763d1 100644 --- a/src/native/managed/cdac/tests/UnitTests/ObjectTests.cs +++ b/src/native/managed/cdac/tests/UnitTests/ObjectTests.cs @@ -383,7 +383,7 @@ public void GetDelegateInfo_Closed(MockTarget.Architecture arch) target: TestTarget, methodPtr: TestMethodPtr, methodPtrAux: 0, - invocationCount: 0); + extraData: 0); }); DelegateInfo info = contract.GetDelegateInfo(delegateAddress); @@ -411,7 +411,7 @@ public void GetDelegateInfo_Open(MockTarget.Architecture arch) target: 0, methodPtr: TestMethodPtr, methodPtrAux: TestMethodPtrAux, - invocationCount: 0); + extraData: 0); }); DelegateInfo info = contract.GetDelegateInfo(delegateAddress); @@ -423,11 +423,11 @@ public void GetDelegateInfo_Open(MockTarget.Architecture arch) [Theory] [ClassData(typeof(MockTarget.StdArch))] - public void GetDelegateInfo_Multicast(MockTarget.Architecture arch) + public void GetDelegateInfo_Unmanaged(MockTarget.Architecture arch) { const ulong TestMethodTable = 0x00000000_10000200; const ulong TestMethodPtr = 0x00000000_aaaa0000; - const long TestInvocationCount = 3; + const long TestExtraData = -1; TargetPointer delegateAddress = default; IObject contract = CreateObjectContract( @@ -439,7 +439,7 @@ public void GetDelegateInfo_Multicast(MockTarget.Architecture arch) target: 0, methodPtr: TestMethodPtr, methodPtrAux: 0, - invocationCount: TestInvocationCount); + extraData: TestExtraData); }); DelegateInfo info = contract.GetDelegateInfo(delegateAddress); From d2a483138ffdc44dca9ca455961e0b7d67a24224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 29 Jun 2026 17:59:56 +0200 Subject: [PATCH 57/77] Fix bad merge --- .../src/System/MulticastDelegate.CoreCLR.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index 34f33925cf90cf..02b86a491523d9 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -212,7 +212,7 @@ private static bool EqualInvocationLists(object[] a, object[] b, int start, int int invocationCount = (int)_extraData; for (int i = invocationCount; --i >= 0;) { - if (v.Equals(invocationList[i])) + if (!v.Equals(invocationList[i])) { continue; } From c85ad6ee41b1d98498ca2b96fced6dee15344316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Mon, 29 Jun 2026 21:03:39 +0200 Subject: [PATCH 58/77] Revert PGO changes --- src/coreclr/vm/jithelpers.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index a98420662be310..2aeb369c9887a2 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1912,13 +1912,13 @@ HCIMPL2(void, JIT_DelegateProfile32, Object *obj, ICorJitInfo::HandleHistogram32 MethodDesc* pRecordedMD = (MethodDesc*)DEFAULT_UNKNOWN_HANDLE; if (!COMDelegate::IsTrueMulticastDelegate(del) && (extraData != DELEGATE_MARKER_UNMANAGEDFPTR)) { - MethodDesc* pMD = (MethodDesc*)extraData; - if ((pMD == nullptr) && (del->GetMethodPtrAux() == (PCODE)NULL)) + MethodDesc* pMD = NULL; + if (del->GetMethodPtrAux() == (PCODE)NULL) { pMD = NonVirtualEntry2MethodDesc(del->GetMethodPtr()); } - if ((pMD != nullptr) && !pMD->GetLoaderAllocator()->IsCollectible() && !pMD->IsDynamicMethod()) + if ((pMD != NULL) && !pMD->GetLoaderAllocator()->IsCollectible() && !pMD->IsDynamicMethod()) { pRecordedMD = pMD; } @@ -1963,13 +1963,13 @@ HCIMPL2(void, JIT_DelegateProfile64, Object *obj, ICorJitInfo::HandleHistogram64 MethodDesc* pRecordedMD = (MethodDesc*)DEFAULT_UNKNOWN_HANDLE; if (!COMDelegate::IsTrueMulticastDelegate(del) && (extraData != DELEGATE_MARKER_UNMANAGEDFPTR)) { - MethodDesc* pMD = (MethodDesc*)extraData; - if ((pMD == nullptr) && (del->GetMethodPtrAux() == (PCODE)NULL)) + MethodDesc* pMD = NULL; + if (del->GetMethodPtrAux() == (PCODE)NULL) { pMD = NonVirtualEntry2MethodDesc(del->GetMethodPtr()); } - if ((pMD != nullptr) && !pMD->GetLoaderAllocator()->IsCollectible() && !pMD->IsDynamicMethod()) + if ((pMD != NULL) && !pMD->GetLoaderAllocator()->IsCollectible() && !pMD->IsDynamicMethod()) { pRecordedMD = pMD; } From 901adc3b80a9bc94936e3b1ce8e92e73afb2d9f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Sat, 4 Jul 2026 03:21:36 +0200 Subject: [PATCH 59/77] Address feedback, centralise unsafe usage --- docs/design/datacontracts/Object.md | 8 ++++- .../src/System/Delegate.CoreCLR.cs | 21 ++++++++---- .../src/System/MulticastDelegate.CoreCLR.cs | 5 ++- src/coreclr/vm/comdelegate.cpp | 33 +++++++++++++++---- src/coreclr/vm/comdelegate.h | 5 +-- src/coreclr/vm/interpexec.cpp | 2 +- src/coreclr/vm/jithelpers.cpp | 4 +-- src/coreclr/vm/virtualcallstub.cpp | 2 +- 8 files changed, 57 insertions(+), 23 deletions(-) diff --git a/docs/design/datacontracts/Object.md b/docs/design/datacontracts/Object.md index a533e3e2587971..2cdd3ff09828ac 100644 --- a/docs/design/datacontracts/Object.md +++ b/docs/design/datacontracts/Object.md @@ -89,6 +89,11 @@ Global variables used: | `SyncBlockHashCodeMask` | uint32 | Mask for extracting the hash code from the sync block value. | | `SyncBlockIndexMask` | uint32 | The mask for sync block index field. | +Contract Constants: +| Name | Type | Purpose | Value | +| --- | --- | --- | --- | +| `unmanagedMarker` | nint | Sentinel value for detecting unmanaged pointer delegates. | `-1` | + Contracts used: | Contract Name | | --- | @@ -233,8 +238,9 @@ DelegateInfo GetDelegateInfo(TargetPointer address) isMulticast = rts.IsArray(rts.GetTypeHandle(mt), out _); } + const nint unmanagedMarker = -1; DelegateType delegateType = DelegateType.Unknown; - if (!isMulticast && target.ReadNInt(address + /* Delegate::ExtraData offset */) != -1) + if (!isMulticast && target.ReadNInt(address + /* Delegate::ExtraData offset */) != unmanagedMarker) { delegateType = del.MethodPtrAux == TargetCodePointer.Null ? DelegateType.Closed : DelegateType.Open; } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index cea3173f76f2c3..aa2fb85bbf8298 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -26,7 +26,7 @@ public abstract partial class Delegate : ICloneable, ISerializable // 3. Collectible delegates - LoaderAllocator and such internal object? _helperObject; - // _target is the object we will invoke on; null if static delegate + // _target is the object we will invoke on // Keep _target and _methodPtr next to each other for optimal delegate invoke performance internal object? _target; @@ -123,12 +123,15 @@ private bool TryGetInvocations(out ReadOnlySpan invocations) return false; } - object[] invocationList = (object[])_helperObject!; + Debug.Assert(_helperObject is object[]); + object[] invocationList = (object[])_helperObject; + Debug.Assert(invocationList.Length > 1); - Debug.Assert(invocationList[0] is MulticastDelegate); Debug.Assert((uint)invocationList.Length >= (nuint)_extraData); + Debug.Assert(invocationList[0] is MulticastDelegate); - ref MulticastDelegate first = ref Unsafe.As(ref MemoryMarshal.GetArrayDataReference(invocationList)); + ref object reference = ref MemoryMarshal.GetArrayDataReference(invocationList); + ref MulticastDelegate first = ref Unsafe.As(ref reference); invocations = MemoryMarshal.CreateReadOnlySpan(ref first, (int)_extraData); return true; } @@ -184,7 +187,7 @@ public override unsafe bool Equals([NotNullWhen(true)] object? obj) _methodPtrAux == other._methodPtrAux; } - // do an optimistic check first. This is hopefully cheap enough to be worth + // Try checking full equality for other types, this is hopefully cheap enough to be worth it if (_target == other._target && _methodPtr == other._methodPtr && _methodPtrAux == other._methodPtrAux) @@ -262,7 +265,7 @@ MethodInfo GetMethodImplUncached() MethodInfo method = GetMethodImplCore(); Debug.Assert(method is not null); - Volatile.Write(ref _helperObject, method); + _helperObject = method; return method; } } @@ -528,6 +531,12 @@ private static MulticastDelegate InternalAlloc(RuntimeType type) return Unsafe.As(RuntimeTypeHandle.InternalAlloc(type)); } + internal static unsafe MulticastDelegate InternalAlloc(MethodTable* type) + { + Debug.Assert(RuntimeTypeHandle.GetRuntimeType(type).IsAssignableTo(typeof(MulticastDelegate))); + return Unsafe.As(RuntimeTypeHandle.InternalAllocNoChecks(type)); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static unsafe bool InternalEqualTypes(object a, object b) { diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index 02b86a491523d9..375a10ec1d1a04 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -47,7 +47,7 @@ private static bool TrySetSlot(object?[] a, int index, object o) private unsafe MulticastDelegate NewMulticastDelegate(object[] invocationList, int invocationCount, bool thisIsMultiCastAlready = false) { // First, allocate a new multicast delegate just like this one, i.e. same type as the this object - MulticastDelegate result = Unsafe.As(RuntimeTypeHandle.InternalAllocNoChecks(RuntimeHelpers.GetMethodTable(this))); + MulticastDelegate result = InternalAlloc(RuntimeHelpers.GetMethodTable(this)); // Performance optimization - if this already points to a true multicast delegate, // copy _methodPtr and _methodPtrAux fields rather than calling into the EE to get them @@ -157,8 +157,7 @@ private unsafe MulticastDelegate NewMulticastDelegate(object[] invocationList, i private object[] DeleteFromInvocationList(object[] invocationList, int invocationCount, int deleteIndex, int deleteCount) { Debug.Assert(_helperObject is object[]); - object[]? thisInvocationList = Unsafe.As(_helperObject); - Debug.Assert(thisInvocationList is not null); + object[] thisInvocationList = (object[])_helperObject; int allocCount = thisInvocationList.Length; while (allocCount / 2 >= invocationCount - deleteCount) diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index b27806f2051172..2dc96df6de395d 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1788,7 +1788,7 @@ extern "C" void QCALLTYPE Delegate_Construct(QCall::ObjectHandleOnStack _this, Q END_QCALL; } -MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) +MethodDesc* COMDelegate::GetMethodDesc(OBJECTREF orDelegate) { CONTRACTL { @@ -1811,10 +1811,10 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) // - MethodDesc already cached // we return the method desc for the invoke for the first two cases - if (IsTrueMulticastDelegate(thisDel) || extraData == DELEGATE_MARKER_UNMANAGEDFPTR) + if (!HasSingleTarget(thisDel) || extraData == DELEGATE_MARKER_UNMANAGEDFPTR) return FindDelegateInvokeMethod(thisDel->GetMethodTable()); - return (MethodDesc*)extraData; + return GetCachedMethodDesc(thisDel); } // Next, check for an open delegate @@ -1832,23 +1832,42 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate) return pMethodHandle; } -BOOL COMDelegate::IsTrueMulticastDelegate(DELEGATEREF delegate) +MethodDesc* COMDelegate::GetCachedMethodDesc(DELEGATEREF delegate) { CONTRACTL { NOTHROW; GC_NOTRIGGER; - MODE_ANY; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + _ASSERTE(HasSingleTarget(delegate)); + + INT_PTR extraData = delegate->GetExtraData(); + + _ASSERTE(extraData != DELEGATE_MARKER_UNMANAGEDFPTR); + + return (MethodDesc*)extraData; +} + +BOOL COMDelegate::HasSingleTarget(DELEGATEREF delegate) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; } CONTRACTL_END; OBJECTREF helperObject = delegate->GetHelperObject(); if (helperObject == NULL) { - return false; + return true; } - return helperObject->GetMethodTable()->IsArray(); + return !helperObject->GetMethodTable()->IsArray(); } // Get the cpu stub for a delegate invoke. diff --git a/src/coreclr/vm/comdelegate.h b/src/coreclr/vm/comdelegate.h index d99eaf3e16c2e0..959872c4e0bb92 100644 --- a/src/coreclr/vm/comdelegate.h +++ b/src/coreclr/vm/comdelegate.h @@ -58,9 +58,10 @@ class COMDelegate // Get the cpu stub for a delegate invoke. static Stub* GetInvokeMethodStub(EEImplMethodDesc* pMD); - static MethodDesc * __fastcall GetMethodDesc(OBJECTREF obj); + static MethodDesc* GetMethodDesc(OBJECTREF obj); + static MethodDesc* GetCachedMethodDesc(DELEGATEREF delegate); - static BOOL IsTrueMulticastDelegate(DELEGATEREF delegate); + static BOOL HasSingleTarget(DELEGATEREF delegate); // Throw if the method violates any usage restrictions // for UnmanagedCallersOnlyAttribute. diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index e39ba244675a3f..e4d2aa2b90b29a 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -3408,7 +3408,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr if (isOpenVirtual) { - targetMethod = COMDelegate::GetMethodDesc(*delegateObj); + targetMethod = COMDelegate::GetCachedMethodDesc(*delegateObj); OBJECTREF *pThisArg = LOCAL_VAR_ADDR(callArgsOffset + INTERP_STACK_SLOT_SIZE, OBJECTREF); NULL_CHECK(*pThisArg); targetMethod = CallWithSEHWrapper( diff --git a/src/coreclr/vm/jithelpers.cpp b/src/coreclr/vm/jithelpers.cpp index c0b465db49ff6a..52241ce4b506e6 100644 --- a/src/coreclr/vm/jithelpers.cpp +++ b/src/coreclr/vm/jithelpers.cpp @@ -1911,7 +1911,7 @@ HCIMPL2(void, JIT_DelegateProfile32, Object *obj, ICorJitInfo::HandleHistogram32 INT_PTR extraData = del->GetExtraData(); MethodDesc* pRecordedMD = (MethodDesc*)DEFAULT_UNKNOWN_HANDLE; - if (!COMDelegate::IsTrueMulticastDelegate(del) && (extraData != DELEGATE_MARKER_UNMANAGEDFPTR)) + if (COMDelegate::HasSingleTarget(del) && (extraData != DELEGATE_MARKER_UNMANAGEDFPTR)) { MethodDesc* pMD = NULL; if (del->GetMethodPtrAux() == (PCODE)NULL) @@ -1962,7 +1962,7 @@ HCIMPL2(void, JIT_DelegateProfile64, Object *obj, ICorJitInfo::HandleHistogram64 INT_PTR extraData = del->GetExtraData(); MethodDesc* pRecordedMD = (MethodDesc*)DEFAULT_UNKNOWN_HANDLE; - if (!COMDelegate::IsTrueMulticastDelegate(del) && (extraData != DELEGATE_MARKER_UNMANAGEDFPTR)) + if (COMDelegate::HasSingleTarget(del) && (extraData != DELEGATE_MARKER_UNMANAGEDFPTR)) { MethodDesc* pMD = NULL; if (del->GetMethodPtrAux() == (PCODE)NULL) diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index 117cc741deff74..42e28dac668962 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -1475,7 +1475,7 @@ extern "C" PCODE CID_VirtualOpenDelegateDispatchWorker(TransitionBlock * pTransi _ASSERTE(!"Throw returned"); } - MethodDesc *pTargetMD = (MethodDesc*)((DELEGATEREF)delegateObj)->GetExtraData(); + MethodDesc *pTargetMD = COMDelegate::GetCachedMethodDesc((DELEGATEREF)delegateObj); pSDFrame->SetFunction(pTargetMD); pSDFrame->Push(CURRENT_THREAD); From eceec722e46434370b1d605948ff00d5c03e7555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Sat, 4 Jul 2026 04:07:29 +0200 Subject: [PATCH 60/77] Use object --- .../src/System/Delegate.CoreCLR.cs | 50 +++++++++++++------ 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index aa2fb85bbf8298..232327c472680e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -48,8 +48,8 @@ public abstract partial class Delegate : ICloneable, ISerializable public partial bool HasSingleTarget => _helperObject is null || _helperObject.GetType() != typeof(object[]); public object? Target => - TryGetInvocations(out ReadOnlySpan invocations) - ? invocations[^1].Target + TryGetInvocations(out ReadOnlySpan invocations) + ? ((Delegate)invocations[^1]).Target : IsUnmanagedFunctionPtr || _methodPtrAux != 0 ? null : _target; private unsafe MethodDesc* MethodDesc @@ -110,12 +110,23 @@ protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Al } // This method returns the Invocation list of this multicast delegate. - public Delegate[] GetInvocationList() => - TryGetInvocations(out ReadOnlySpan invocations) - ? ((ReadOnlySpan)invocations).ToArray() : [this]; + public Delegate[] GetInvocationList() + { + if (!TryGetInvocations(out ReadOnlySpan invocations)) + { + return [this]; + } + + Delegate[] invocationList = new Delegate[invocations.Length]; + for (int i = 0; i < invocations.Length; i++) + { + invocationList[i] = (Delegate)invocations[i]; + } + return invocationList; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool TryGetInvocations(out ReadOnlySpan invocations) + private bool TryGetInvocations(out ReadOnlySpan invocations) { if (HasSingleTarget) { @@ -130,19 +141,17 @@ private bool TryGetInvocations(out ReadOnlySpan invocations) Debug.Assert((uint)invocationList.Length >= (nuint)_extraData); Debug.Assert(invocationList[0] is MulticastDelegate); - ref object reference = ref MemoryMarshal.GetArrayDataReference(invocationList); - ref MulticastDelegate first = ref Unsafe.As(ref reference); - invocations = MemoryMarshal.CreateReadOnlySpan(ref first, (int)_extraData); + invocations = new ReadOnlySpan(invocationList, 0, (int)_extraData); return true; } // Used by delegate invocation list enumerator private Delegate? TryGetAt(int index) { - if (TryGetInvocations(out ReadOnlySpan invocations)) + if (TryGetInvocations(out ReadOnlySpan invocations)) { if ((uint)index < (uint)invocations.Length) - return invocations[index]; + return (Delegate)invocations[index]; } else if (index == 0) { @@ -175,9 +184,18 @@ public override unsafe bool Equals([NotNullWhen(true)] object? obj) Debug.Assert(obj is Delegate, "Shouldn't have failed here since we already checked the types are the same!"); Delegate other = Unsafe.As(obj); - if (TryGetInvocations(out ReadOnlySpan invocations)) + if (TryGetInvocations(out ReadOnlySpan invocations)) { - return other.TryGetInvocations(out ReadOnlySpan otherInvocations) && invocations.SequenceEqual(otherInvocations); + if (!other.TryGetInvocations(out ReadOnlySpan otherInvocations) || invocations.Length != otherInvocations.Length) + return false; + + for (int i = 0; i < invocations.Length; i++) + { + if (!invocations[i].Equals(otherInvocations[i])) + return false; + } + + return true; } if (IsUnmanagedFunctionPtr) @@ -228,7 +246,7 @@ public override unsafe bool Equals([NotNullWhen(true)] object? obj) public override unsafe int GetHashCode() { - if (TryGetInvocations(out ReadOnlySpan invocations)) + if (TryGetInvocations(out ReadOnlySpan invocations)) { int hash = 0; foreach (MulticastDelegate multicastDelegate in invocations) @@ -253,8 +271,8 @@ public override unsafe int GetHashCode() protected virtual MethodInfo GetMethodImpl() { - return TryGetInvocations(out ReadOnlySpan invocations) - ? invocations[^1].Method + return TryGetInvocations(out ReadOnlySpan invocations) + ? ((Delegate)invocations[^1]).Method : _helperObject as MethodInfo ?? GetMethodImplUncached(); [MethodImpl(MethodImplOptions.NoInlining)] From 5ed01195b5326d5cc0968fecd173fbcdf73b8321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Fri, 10 Jul 2026 23:36:39 +0200 Subject: [PATCH 61/77] Address review --- docs/design/datacontracts/Object.md | 8 +- .../src/System/Delegate.CoreCLR.cs | 101 ++++++++---------- .../src/System/MulticastDelegate.CoreCLR.cs | 5 +- src/coreclr/vm/comdelegate.cpp | 6 +- 4 files changed, 52 insertions(+), 68 deletions(-) diff --git a/docs/design/datacontracts/Object.md b/docs/design/datacontracts/Object.md index 2cdd3ff09828ac..ca0b0585b8321f 100644 --- a/docs/design/datacontracts/Object.md +++ b/docs/design/datacontracts/Object.md @@ -92,7 +92,7 @@ Global variables used: Contract Constants: | Name | Type | Purpose | Value | | --- | --- | --- | --- | -| `unmanagedMarker` | nint | Sentinel value for detecting unmanaged pointer delegates. | `-1` | +| `UnmanagedMarker` | nint | Sentinel value for detecting unmanaged pointer delegates. | `-1` | Contracts used: | Contract Name | @@ -228,7 +228,7 @@ DelegateInfo GetDelegateInfo(TargetPointer address) // Check for multicast and unmanaged first. bool isMulticast = false; TargetPointer helperObject = target.ReadPointer(address + /* Delegate::HelperObject offset */); - if (helperObject == TargetPointer.Null) + if (helperObject != TargetPointer.Null) { IRuntimeTypeSystem rts = target.Contracts.RuntimeTypeSystem; @@ -238,9 +238,9 @@ DelegateInfo GetDelegateInfo(TargetPointer address) isMulticast = rts.IsArray(rts.GetTypeHandle(mt), out _); } - const nint unmanagedMarker = -1; + const nint UnmanagedMarker = -1; DelegateType delegateType = DelegateType.Unknown; - if (!isMulticast && target.ReadNInt(address + /* Delegate::ExtraData offset */) != unmanagedMarker) + if (!isMulticast && target.ReadNInt(address + /* Delegate::ExtraData offset */) != UnmanagedMarker) { delegateType = del.MethodPtrAux == TargetCodePointer.Null ? DelegateType.Closed : DelegateType.Open; } diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 232327c472680e..9b3bd808a0417e 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -50,7 +50,7 @@ public abstract partial class Delegate : ICloneable, ISerializable public object? Target => TryGetInvocations(out ReadOnlySpan invocations) ? ((Delegate)invocations[^1]).Target - : IsUnmanagedFunctionPtr || _methodPtrAux != 0 ? null : _target; + : _methodPtrAux != 0 ? null : _target; private unsafe MethodDesc* MethodDesc { @@ -169,9 +169,9 @@ private bool TryGetInvocations(out ReadOnlySpan invocations) return invoke.Invoke(this, BindingFlags.Default, null, args, null); } - // equals returns true IIF the delegate is not null and has the - // same target, method and invocation list as this object - public override unsafe bool Equals([NotNullWhen(true)] object? obj) + // Equals returns true IIF the delegate is not null and has the + // same target, method and invocation list as this object. + public sealed override unsafe bool Equals([NotNullWhen(true)] object? obj) { if (obj == null) return false; @@ -180,71 +180,64 @@ public override unsafe bool Equals([NotNullWhen(true)] object? obj) if (!InternalEqualTypes(this, obj)) return false; - // Since this is a Delegate and we know the types are the same, obj should also be a Delegate + // Since this is a Delegate, and we know the types are the same, obj should also be a Delegate Debug.Assert(obj is Delegate, "Shouldn't have failed here since we already checked the types are the same!"); Delegate other = Unsafe.As(obj); - if (TryGetInvocations(out ReadOnlySpan invocations)) - { - if (!other.TryGetInvocations(out ReadOnlySpan otherInvocations) || invocations.Length != otherInvocations.Length) - return false; - - for (int i = 0; i < invocations.Length; i++) - { - if (!invocations[i].Equals(otherInvocations[i])) - return false; - } - - return true; - } - - if (IsUnmanagedFunctionPtr) - { - return other.IsUnmanagedFunctionPtr && - _methodPtr == other._methodPtr && - _methodPtrAux == other._methodPtrAux; - } - - // Try checking full equality for other types, this is hopefully cheap enough to be worth it - if (_target == other._target && - _methodPtr == other._methodPtr && - _methodPtrAux == other._methodPtrAux) - return true; - - // even though the fields were not all equals the delegates may still match - // When target carries the delegate itself the 2 targets (delegates) may be different instances - // but the delegates are logically the same - // It may also happen that the method pointer was not jitted when creating one delegate and jitted in the other - // if that's the case the delegates may still be equals but we need to make a more complicated check - + // Check closed delegates first if (_methodPtrAux == 0) { + // different delegate kind if (other._methodPtrAux != 0) - return false; // different delegate kind + return false; - // they are both closed over the first arg + // different instances if (_target != other._target) return false; - // fall through method handle check + if (_methodPtr == other._methodPtr) + return true; } else { + // different delegate kinds if (other._methodPtrAux == 0) - return false; // different delegate kind + return false; - // Ignore the target as it will be the delegate instance, though it may be a different one + // multicast + if (TryGetInvocations(out ReadOnlySpan invocations)) + { + if (!other.TryGetInvocations(out ReadOnlySpan otherInvocations) || invocations.Length != otherInvocations.Length) + return false; + + for (int i = 0; i < invocations.Length; i++) + { + if (!invocations[i].Equals(otherInvocations[i])) + return false; + } - if (_methodPtrAux == other._methodPtrAux) return true; + } - // fall through method handle check + // unmanaged + if (IsUnmanagedFunctionPtr) + { + return other.IsUnmanagedFunctionPtr && + _methodPtr == other._methodPtr && + _methodPtrAux == other._methodPtrAux; + } + + // both delegates are open + if (_methodPtrAux == other._methodPtrAux) + return true; } + // It's possible that the method pointer was JITted in one delegate but not the other. + // In such case the delegates may still be equal, but we need to check the MethodDescs. return MethodDesc == other.MethodDesc; } - public override unsafe int GetHashCode() + public sealed override unsafe int GetHashCode() { if (TryGetInvocations(out ReadOnlySpan invocations)) { @@ -258,10 +251,10 @@ public override unsafe int GetHashCode() if (IsUnmanagedFunctionPtr) { - return HashCode.Combine(_methodPtr, _methodPtr); + return HashCode.Combine(_methodPtr, _methodPtrAux); } - int hashCode = ((nuint)MethodDesc).GetHashCode(); + int hashCode = HashCode.Combine((nuint)RuntimeHelpers.GetMethodTable(this), (nuint)MethodDesc); if (_methodPtrAux == 0 && _target != null) { hashCode += RuntimeHelpers.GetHashCode(_target) * 33; @@ -299,17 +292,9 @@ private unsafe MethodInfo GetMethodImplCore() // need a proper declaring type instance method on a generic type if (declaringType.IsGenericType) { + Debug.Assert(!IsUnmanagedFunctionPtr); bool isStatic = (RuntimeMethodHandle.GetAttributes(method) & MethodAttributes.Static) != 0; - if (IsUnmanagedFunctionPtr) - { - // we handle unmanaged function pointers here because the generic ones (used for WinRT) would otherwise - // be treated as open delegates, resulting in failure to get the MethodInfo - - // we are returning the 'Invoke' method of this delegate so use this.GetType() for the exact type - RuntimeType reflectedType = (RuntimeType)GetType(); - declaringType = reflectedType; - } - else if (!isStatic) + if (!isStatic) { if (_methodPtrAux == 0) { diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs index 375a10ec1d1a04..4c1232fd1e1360 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs @@ -11,6 +11,7 @@ namespace System { +#pragma warning disable CS0660, CS0661 // Defining operators, but not overriding Equals/GetHashCode [ClassInterface(ClassInterfaceType.None)] [ComVisible(true)] public abstract partial class MulticastDelegate : Delegate @@ -260,9 +261,6 @@ private static bool EqualInvocationLists(object[] a, object[] b, int start, int return this; } - public sealed override bool Equals([NotNullWhen(true)] object? obj) => base.Equals(obj); - public sealed override int GetHashCode() => base.GetHashCode(); - // this should help inlining [DoesNotReturn] [DebuggerNonUserCode] @@ -349,4 +347,5 @@ private void CtorCollectibleVirtualDispatch(object target, IntPtr methodPtr, Int } #pragma warning restore IDE0060 } +#pragma warning restore CS0660, CS0661 } diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 2dc96df6de395d..9a2e957bfa3197 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1806,8 +1806,8 @@ MethodDesc* COMDelegate::GetMethodDesc(OBJECTREF orDelegate) if (extraData != 0) { // this is one of the following: - // - multicast - _invocationList is Array && _invocationCount != 0 - // - unmanaged ftn ptr - _invocationList == null && _invocationCount == -1 + // - multicast - _helperObject is Array && _extraData != 0 + // - unmanaged ftn ptr - _helperObject == null && _extraData == -1 // - MethodDesc already cached // we return the method desc for the invoke for the first two cases @@ -2095,7 +2095,7 @@ extern "C" PCODE QCALLTYPE Delegate_GetMulticastInvokeSlow(MethodTable* pDelegat pCode->EmitADD(); pCode->EmitSTLOC(dwLoopCounterNum); - // compare LoopCounter with InvocationCount. If less then branch to nextDelegate + // compare LoopCounter with _extraData. If less then branch to nextDelegate pCode->EmitLDLOC(dwLoopCounterNum); pCode->EmitLoadThis(); pCode->EmitLDFLD(pCode->GetToken(CoreLibBinder::GetField(FIELD__DELEGATE__EXTRA_DATA))); From f21207bb29036d76f057e50f856bd2436cfca040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Fri, 10 Jul 2026 23:50:53 +0200 Subject: [PATCH 62/77] Fix equivalence handling --- .../System.Private.CoreLib/src/System/Delegate.CoreCLR.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 9b3bd808a0417e..24274ff3271931 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -254,7 +254,8 @@ public sealed override unsafe int GetHashCode() return HashCode.Combine(_methodPtr, _methodPtrAux); } - int hashCode = HashCode.Combine((nuint)RuntimeHelpers.GetMethodTable(this), (nuint)MethodDesc); + MethodTable* methodTable = RuntimeHelpers.GetMethodTable(this); + int hashCode = HashCode.Combine((nuint)(methodTable->HasTypeEquivalence ? null : methodTable), (nuint)MethodDesc); if (_methodPtrAux == 0 && _target != null) { hashCode += RuntimeHelpers.GetHashCode(_target) * 33; From 4a237feef5991381662587f1e5d1a731eb0c0f48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 11 Jul 2026 00:03:41 +0200 Subject: [PATCH 63/77] Fix hash code calculation for delegates with type equivalence --- .../System.Private.CoreLib/src/System/Delegate.CoreCLR.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 24274ff3271931..dc3f31286f20ee 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -255,7 +255,13 @@ public sealed override unsafe int GetHashCode() } MethodTable* methodTable = RuntimeHelpers.GetMethodTable(this); - int hashCode = HashCode.Combine((nuint)(methodTable->HasTypeEquivalence ? null : methodTable), (nuint)MethodDesc); +#if FEATURE_TYPEEQUIVALENCE + if (methodTable->HasTypeEquivalence) + { + methodTable = null; + } +#endif + int hashCode = HashCode.Combine((nuint)methodTable, (nuint)MethodDesc); if (_methodPtrAux == 0 && _target != null) { hashCode += RuntimeHelpers.GetHashCode(_target) * 33; From 6f6d24b4a459ef1d5df167870d0891e08c3dca2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 11 Jul 2026 01:34:36 +0200 Subject: [PATCH 64/77] Update src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs Co-authored-by: Jan Kotas --- .../System.Private.CoreLib/src/System/Delegate.CoreCLR.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index dc3f31286f20ee..1b08bcfaf740bf 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -223,7 +223,6 @@ public sealed override unsafe bool Equals([NotNullWhen(true)] object? obj) if (IsUnmanagedFunctionPtr) { return other.IsUnmanagedFunctionPtr && - _methodPtr == other._methodPtr && _methodPtrAux == other._methodPtrAux; } From 3ab6fb463468c6c5ab2797ad9a13a9d01b3a9521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 11 Jul 2026 01:37:36 +0200 Subject: [PATCH 65/77] Rename --- .../System.Private.CoreLib/src/System/Delegate.CoreCLR.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index 1b08bcfaf740bf..b7e39b331fe5be 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -272,14 +272,14 @@ protected virtual MethodInfo GetMethodImpl() { return TryGetInvocations(out ReadOnlySpan invocations) ? ((Delegate)invocations[^1]).Method - : _helperObject as MethodInfo ?? GetMethodImplUncached(); + : _helperObject as MethodInfo ?? GetMethodImplSlow(); [MethodImpl(MethodImplOptions.NoInlining)] - MethodInfo GetMethodImplUncached() + MethodInfo GetMethodImplSlow() { Debug.Assert(HasSingleTarget); - MethodInfo method = GetMethodImplCore(); + MethodInfo method = GetMethodImplUncached(); Debug.Assert(method is not null); _helperObject = method; @@ -287,7 +287,7 @@ MethodInfo GetMethodImplUncached() } } - private unsafe MethodInfo GetMethodImplCore() + private unsafe MethodInfo GetMethodImplUncached() { // should be handled by GetMethodImpl Debug.Assert(HasSingleTarget); From a87c55b97a70d8d62a734b40032faecf879290b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 11 Jul 2026 02:22:51 +0200 Subject: [PATCH 66/77] Simplify GetMethodImpl by removing GetMethodImplSlow Refactor GetMethodImpl to streamline method retrieval. --- .../src/System/Delegate.CoreCLR.cs | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index b7e39b331fe5be..f642f04c699a37 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -272,21 +272,10 @@ protected virtual MethodInfo GetMethodImpl() { return TryGetInvocations(out ReadOnlySpan invocations) ? ((Delegate)invocations[^1]).Method - : _helperObject as MethodInfo ?? GetMethodImplSlow(); - - [MethodImpl(MethodImplOptions.NoInlining)] - MethodInfo GetMethodImplSlow() - { - Debug.Assert(HasSingleTarget); - - MethodInfo method = GetMethodImplUncached(); - Debug.Assert(method is not null); - - _helperObject = method; - return method; - } + : _helperObject as MethodInfo ?? GetMethodImplUncached(); } + [MethodImpl(MethodImplOptions.NoInlining)] private unsafe MethodInfo GetMethodImplUncached() { // should be handled by GetMethodImpl @@ -343,7 +332,12 @@ private unsafe MethodInfo GetMethodImplUncached() } } } - return (MethodInfo)RuntimeType.GetMethodBase(declaringType, method)!; + + MethodInfo? method = (MethodInfo?)RuntimeType.GetMethodBase(declaringType, method); + Debug.Assert(method is not null); + + _helperObject = method; + return method; } [RequiresUnreferencedCode("The target method might be removed")] From 28de884cfc350d071c0f40f90848ff21fe090c5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 11 Jul 2026 05:27:20 +0200 Subject: [PATCH 67/77] Update Delegate.cs --- .../Data/Delegate.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Delegate.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Delegate.cs index 48fe0051ae57d0..bf17bcf89f1057 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Delegate.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/Delegate.cs @@ -6,8 +6,6 @@ namespace Microsoft.Diagnostics.DataContractReader.Data; [CdacType(nameof(DataType.Delegate))] internal sealed partial class Delegate : IData { - public const long UnmanagedMarker = -1; - [Field] public TargetPointer HelperObject { get; } [Field] public TargetPointer Target { get; } [Field] public TargetCodePointer MethodPtr { get; } From 9982821ee6ce5684957a857e26dc183b9f8d6b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 11 Jul 2026 05:28:11 +0200 Subject: [PATCH 68/77] Replace Data.Delegate.UnmanagedMarker with constant --- .../Contracts/Object_1.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Object_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Object_1.cs index 031fbc3c8d180f..174ac5434e966c 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Object_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Object_1.cs @@ -175,8 +175,9 @@ public DelegateInfo GetDelegateInfo(TargetPointer address) isMulticast = typeSystemContract.IsArray(typeSystemContract.GetTypeHandle(mt), out _); } + const long UnmanagedMarker = -1; DelegateType delegateType = DelegateType.Unknown; - if (!isMulticast && del.ExtraData.Value != Data.Delegate.UnmanagedMarker) + if (!isMulticast && del.ExtraData.Value != UnmanagedMarker) { delegateType = del.MethodPtrAux == TargetCodePointer.Null ? DelegateType.Closed : DelegateType.Open; } From d771cf46983c8a73ba78d0e7b44021051620a9c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 11 Jul 2026 05:53:34 +0200 Subject: [PATCH 69/77] Update src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs Co-authored-by: Jan Kotas --- .../src/System/Delegate.CoreCLR.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index f642f04c699a37..e7be1142774942 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -248,11 +248,6 @@ public sealed override unsafe int GetHashCode() return hash; } - if (IsUnmanagedFunctionPtr) - { - return HashCode.Combine(_methodPtr, _methodPtrAux); - } - MethodTable* methodTable = RuntimeHelpers.GetMethodTable(this); #if FEATURE_TYPEEQUIVALENCE if (methodTable->HasTypeEquivalence) @@ -260,6 +255,11 @@ public sealed override unsafe int GetHashCode() methodTable = null; } #endif + + if (IsUnmanagedFunctionPtr) + { + return HashCode.Combine((nuint)methodTable, _methodPtrAux); + } int hashCode = HashCode.Combine((nuint)methodTable, (nuint)MethodDesc); if (_methodPtrAux == 0 && _target != null) { From 30bdb4c66454b2676be1661c3104552df68b0b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 11 Jul 2026 05:54:55 +0200 Subject: [PATCH 70/77] Seal Equals and GetHashCode methods in Delegate class --- .../nativeaot/System.Private.CoreLib/src/System/Delegate.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs index d0a9087237bb94..9252961faeb0ea 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs @@ -694,7 +694,7 @@ public Delegate[] GetInvocationList() return new Delegate[] { this }; } - public override bool Equals([NotNullWhen(true)] object? obj) + public sealed override bool Equals([NotNullWhen(true)] object? obj) { if (obj == null) return false; @@ -749,7 +749,7 @@ public override bool Equals([NotNullWhen(true)] object? obj) return object.ReferenceEquals(_target, d._target); } - public override int GetHashCode() + public sealed override int GetHashCode() { if (_helperObject is Wrapper[] invocationList) { From 7994cb8bcf29515a015232ab204bd615c8718694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 11 Jul 2026 06:13:56 +0200 Subject: [PATCH 71/77] Refactor UnmanagedMarker declaration position Moved UnmanagedMarker constant declaration to the top of the struct. --- .../Contracts/Object_1.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Object_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Object_1.cs index 174ac5434e966c..54a5cec221efff 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Object_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/Object_1.cs @@ -9,6 +9,8 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts; internal readonly struct Object_1 : IObject { + private const long UnmanagedMarker = -1; + private readonly Target _target; private readonly ulong _methodTableOffset; private readonly byte _objectToMethodTableUnmask; @@ -175,7 +177,6 @@ public DelegateInfo GetDelegateInfo(TargetPointer address) isMulticast = typeSystemContract.IsArray(typeSystemContract.GetTypeHandle(mt), out _); } - const long UnmanagedMarker = -1; DelegateType delegateType = DelegateType.Unknown; if (!isMulticast && del.ExtraData.Value != UnmanagedMarker) { From d2c2fed7c14e225604429a43120fc4016c15b76d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 11 Jul 2026 06:15:09 +0200 Subject: [PATCH 72/77] Rename variable fRet to pMD in Delegate_GetMethodDesc --- src/coreclr/vm/comdelegate.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 9a2e957bfa3197..08f827fa32de1f 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1983,17 +1983,17 @@ extern "C" MethodDesc* QCALLTYPE Delegate_GetMethodDesc(QCall::ObjectHandleOnSta { QCALL_CONTRACT; - MethodDesc* fRet = nullptr; + MethodDesc* pMD = nullptr; BEGIN_QCALL; GCX_COOP(); - fRet = COMDelegate::GetMethodDesc(instance.Get()); + pMD = COMDelegate::GetMethodDesc(instance.Get()); END_QCALL; - return fRet; + return pMD; } FCIMPL1(MethodDesc*, COMDelegate::GetInvokeMethod, MethodTable* pDelegateMT) From 944835124ff80603fc4b0f188cceac0b1cab9628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 11 Jul 2026 06:18:03 +0200 Subject: [PATCH 73/77] Refactor GetMethodDesc for clarity Simplified GetMethodDesc by removing unnecessary variable and conditional. --- .../System.Private.CoreLib/src/System/Delegate.CoreCLR.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index e7be1142774942..ed41d670001a4a 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -629,12 +629,7 @@ internal static unsafe IRuntimeMethodInfo CreateMethodInfo(MethodDesc* methodDes private unsafe MethodDesc* GetMethodDesc() { Delegate instance = this; - MethodDesc* methodDesc = GetMethodDesc(ObjectHandleOnStack.Create(ref instance)); - if (!IsUnmanagedFunctionPtr) - { - _extraData = (nint)methodDesc; - } - return methodDesc; + return GetMethodDesc(ObjectHandleOnStack.Create(ref instance)); } [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_GetMethodDesc")] From 49147343bacce7c4d1e3287bb6eba74a798e8710 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 11 Jul 2026 06:24:08 +0200 Subject: [PATCH 74/77] Update src/coreclr/vm/comdelegate.cpp Co-authored-by: Jan Kotas --- src/coreclr/vm/comdelegate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 08f827fa32de1f..d8059050be2650 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1814,7 +1814,7 @@ MethodDesc* COMDelegate::GetMethodDesc(OBJECTREF orDelegate) if (!HasSingleTarget(thisDel) || extraData == DELEGATE_MARKER_UNMANAGEDFPTR) return FindDelegateInvokeMethod(thisDel->GetMethodTable()); - return GetCachedMethodDesc(thisDel); + return (MethodDesc*)extraData; } // Next, check for an open delegate From 8db2814303a0b6242f9f7561e644b1228cbdae2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= <35800402+MichalPetryka@users.noreply.github.com> Date: Sat, 11 Jul 2026 07:19:31 +0200 Subject: [PATCH 75/77] Rename variable for clarity in Delegate.CoreCLR.cs --- .../System.Private.CoreLib/src/System/Delegate.CoreCLR.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index ed41d670001a4a..b27a61127e8331 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -333,11 +333,11 @@ private unsafe MethodInfo GetMethodImplUncached() } } - MethodInfo? method = (MethodInfo?)RuntimeType.GetMethodBase(declaringType, method); - Debug.Assert(method is not null); + MethodInfo? methodInfo = (MethodInfo?)RuntimeType.GetMethodBase(declaringType, method); + Debug.Assert(methodInfo is not null); - _helperObject = method; - return method; + _helperObject = methodInfo; + return methodInfo; } [RequiresUnreferencedCode("The target method might be removed")] From f8cb0de83a42374511eb2474627940df89d733b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Tue, 14 Jul 2026 08:55:42 +0200 Subject: [PATCH 76/77] Address feedback --- docs/design/datacontracts/Object.md | 3 ++- .../src/System/Delegate.CoreCLR.cs | 26 ++++++++++--------- src/coreclr/clr.featuredefines.props | 9 +++++++ src/coreclr/vm/comdelegate.cpp | 2 +- src/coreclr/vm/comdelegate.h | 2 +- src/coreclr/vm/interpexec.cpp | 2 +- src/coreclr/vm/stubmgr.cpp | 5 ++-- src/coreclr/vm/virtualcallstub.cpp | 2 +- .../System/DelegateTests.cs | 21 +++++++++++++++ 9 files changed, 52 insertions(+), 20 deletions(-) diff --git a/docs/design/datacontracts/Object.md b/docs/design/datacontracts/Object.md index ca0b0585b8321f..29d946f0a759ef 100644 --- a/docs/design/datacontracts/Object.md +++ b/docs/design/datacontracts/Object.md @@ -66,10 +66,11 @@ Data descriptors used: | `SyncTableEntry` | `SyncBlock` | `SyncBlock` corresponding to the entry | | `ObjectHeader` | `SyncBlockValue` | Sync block value from the object header | | `SyncBlock` | `HashCode` | Hash code stored in the sync block | +| `Delegate` | `HelperObject` | Invocation list for multicast, MethodInfo otherwise | | `Delegate` | `Target` | Bound `this` reference for closed delegates | | `Delegate` | `MethodPtr` | Primary method pointer | | `Delegate` | `MethodPtrAux` | Auxiliary method pointer | -| `Delegate` | `InvocationCount` | Invocation count (non-zero for multicast/wrapper/unmanaged/special delegates) | +| `Delegate` | `ExtraData` | Invocation count for multicast, UnmanagedMarker for unmanaged, MethodDesc otherwise | | `ContinuationObject` | `Next` | Pointer to the next continuation in the linked list | | `ContinuationObject` | `ResumeInfo` | Pointer to the `ResumeInfo` for this suspension point (may be null) | | `ContinuationObject` | `State` | State index identifying the suspension point within the resumed method | diff --git a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs index b27a61127e8331..5f62281f3aa994 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs @@ -9,7 +9,6 @@ using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; -using System.Threading; namespace System { @@ -45,12 +44,14 @@ public abstract partial class Delegate : ICloneable, ISerializable private bool IsUnmanagedFunctionPtr => _extraData == UnmanagedMarker; + private bool IsClosed => _methodPtrAux == 0; + public partial bool HasSingleTarget => _helperObject is null || _helperObject.GetType() != typeof(object[]); public object? Target => TryGetInvocations(out ReadOnlySpan invocations) ? ((Delegate)invocations[^1]).Target - : _methodPtrAux != 0 ? null : _target; + : IsClosed ? _target : null; private unsafe MethodDesc* MethodDesc { @@ -185,10 +186,10 @@ public sealed override unsafe bool Equals([NotNullWhen(true)] object? obj) Delegate other = Unsafe.As(obj); // Check closed delegates first - if (_methodPtrAux == 0) + if (IsClosed) { // different delegate kind - if (other._methodPtrAux != 0) + if (!other.IsClosed) return false; // different instances @@ -201,7 +202,7 @@ public sealed override unsafe bool Equals([NotNullWhen(true)] object? obj) else { // different delegate kinds - if (other._methodPtrAux == 0) + if (other.IsClosed) return false; // multicast @@ -226,9 +227,13 @@ public sealed override unsafe bool Equals([NotNullWhen(true)] object? obj) _methodPtrAux == other._methodPtrAux; } + // Under cached interface dispatch we might see the shared CID_VirtualOpenDelegateDispatch stub. + // Fallback to desc comparison in such case for correctness. +#if !FEATURE_CACHED_INTERFACE_DISPATCH // both delegates are open if (_methodPtrAux == other._methodPtrAux) return true; +#endif } // It's possible that the method pointer was JITted in one delegate but not the other. @@ -256,12 +261,9 @@ public sealed override unsafe int GetHashCode() } #endif - if (IsUnmanagedFunctionPtr) - { - return HashCode.Combine((nuint)methodTable, _methodPtrAux); - } - int hashCode = HashCode.Combine((nuint)methodTable, (nuint)MethodDesc); - if (_methodPtrAux == 0 && _target != null) + nuint targetMethod = IsUnmanagedFunctionPtr ? (nuint)_methodPtrAux : (nuint)MethodDesc; + int hashCode = HashCode.Combine((nuint)methodTable, targetMethod); + if (IsClosed) { hashCode += RuntimeHelpers.GetHashCode(_target) * 33; } @@ -291,7 +293,7 @@ private unsafe MethodInfo GetMethodImplUncached() bool isStatic = (RuntimeMethodHandle.GetAttributes(method) & MethodAttributes.Static) != 0; if (!isStatic) { - if (_methodPtrAux == 0) + if (IsClosed) { // The target may be of a derived type that doesn't have visibility onto the // target method. We don't want to call RuntimeType.GetMethodBase below with that diff --git a/src/coreclr/clr.featuredefines.props b/src/coreclr/clr.featuredefines.props index af8673df1c3b0e..a37bacbfd1807b 100644 --- a/src/coreclr/clr.featuredefines.props +++ b/src/coreclr/clr.featuredefines.props @@ -62,6 +62,14 @@ true + + true + + + + true + + $(DefineConstants);FEATURE_JAVAMARSHAL $(DefineConstants);FEATURE_COMWRAPPERS @@ -74,6 +82,7 @@ $(DefineConstants);FEATURE_TYPEEQUIVALENCE $(DefineConstants);FEATURE_INTERPRETER $(DefineConstants);FEATURE_DYNAMIC_CODE_COMPILED + $(DefineConstants);FEATURE_CACHED_INTERFACE_DISPATCH $(DefineConstants);FEATURE_PORTABLE_ENTRYPOINTS $(DefineConstants);FEATURE_PORTABLE_HELPERS $(DefineConstants);PROFILING_SUPPORTED diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index d8059050be2650..304dad972e790c 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -1832,7 +1832,7 @@ MethodDesc* COMDelegate::GetMethodDesc(OBJECTREF orDelegate) return pMethodHandle; } -MethodDesc* COMDelegate::GetCachedMethodDesc(DELEGATEREF delegate) +MethodDesc* COMDelegate::GetMethodDescForOpenVirtualDelegate(DELEGATEREF delegate) { CONTRACTL { diff --git a/src/coreclr/vm/comdelegate.h b/src/coreclr/vm/comdelegate.h index 959872c4e0bb92..102d157d3782f7 100644 --- a/src/coreclr/vm/comdelegate.h +++ b/src/coreclr/vm/comdelegate.h @@ -59,7 +59,7 @@ class COMDelegate static Stub* GetInvokeMethodStub(EEImplMethodDesc* pMD); static MethodDesc* GetMethodDesc(OBJECTREF obj); - static MethodDesc* GetCachedMethodDesc(DELEGATEREF delegate); + static MethodDesc* GetMethodDescForOpenVirtualDelegate(DELEGATEREF delegate); static BOOL HasSingleTarget(DELEGATEREF delegate); diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 41959bbba9e777..c39911514c9d72 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -3395,7 +3395,7 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr if (isOpenVirtual) { - targetMethod = COMDelegate::GetCachedMethodDesc(*delegateObj); + targetMethod = COMDelegate::GetMethodDescForOpenVirtualDelegate(*delegateObj); OBJECTREF *pThisArg = LOCAL_VAR_ADDR(callArgsOffset + INTERP_STACK_SLOT_SIZE, OBJECTREF); NULL_CHECK(*pThisArg); targetMethod = CallWithSEHWrapper( diff --git a/src/coreclr/vm/stubmgr.cpp b/src/coreclr/vm/stubmgr.cpp index 250ec35a5a7db0..d874b9614aaeaf 100644 --- a/src/coreclr/vm/stubmgr.cpp +++ b/src/coreclr/vm/stubmgr.cpp @@ -463,7 +463,7 @@ BOOL StubManager::CheckIsStub_Worker(PCODE stubStartAddress) EX_TRY #endif { - SUPPORTS_DAC; + SUPPORTS_DAC; #ifndef DACCESS_COMPILE // Use CheckIsStub_Internal may AV. That's ok. @@ -1219,8 +1219,7 @@ BOOL StubLinkStubManager::TraceDelegateObject(BYTE* pbDel, TraceDestination *tra // - Instance open non-virt // - Instance open virtual // - Static closed - // - Static opened - // - Instance open virtual + // - Static open // - Unmanaged FtnPtr // if _methodPtrAux is 0 the target is in _methodPtr, otherwise the target is _methodPtrAux diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index 42e28dac668962..d0d194b260f4b9 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -1475,7 +1475,7 @@ extern "C" PCODE CID_VirtualOpenDelegateDispatchWorker(TransitionBlock * pTransi _ASSERTE(!"Throw returned"); } - MethodDesc *pTargetMD = COMDelegate::GetCachedMethodDesc((DELEGATEREF)delegateObj); + MethodDesc *pTargetMD = COMDelegate::GetMethodDescForOpenVirtualDelegate((DELEGATEREF)delegateObj); pSDFrame->SetFunction(pTargetMD); pSDFrame->Push(CURRENT_THREAD); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DelegateTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DelegateTests.cs index f9d54aea1c5d7a..6494af3a1341cd 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DelegateTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DelegateTests.cs @@ -521,6 +521,21 @@ public static void SameGenericMethodObtainedViaDelegateAndReflectionAreSameForSt Assert.Equal(m1.MethodHandle.Value, m2.MethodHandle.Value); } + [Fact] + public static void DifferentOpenVirtualDelegates() + { + MethodInfo m1 = typeof(OpenVirtualClass).GetMethod(nameof(OpenVirtualClass.M1), BindingFlags.Instance | BindingFlags.NonPublic); + MethodInfo m2 = typeof(OpenVirtualClass).GetMethod(nameof(OpenVirtualClass.M2), BindingFlags.Instance | BindingFlags.NonPublic); + + Delegate a = m1.CreateDelegate>(); + Delegate b = m2.CreateDelegate>(); + + Assert.False(a.Equals(b)); + + Assert.Equal(m1, a.Method); + Assert.Equal(m2, b.Method); + } + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsTypeEquivalenceSupported))] public static void TypeEquivalentDelegatesPointingToSameMethod_AreEqualAndHaveSameHashCode() { @@ -550,6 +565,12 @@ class ClassG { internal void M() { } } struct StructG { internal void M() { } } + class OpenVirtualClass + { + internal virtual void M1() { } + internal virtual void M2() { } + } + private delegate void IntIntDelegate(int expected, int actual); private delegate void IntIntDelegateWithDefault(int expected, int actual = 7); From 88a0b0be80eaa7c2556672e4296d01dbd65448d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Petryka?= Date: Tue, 14 Jul 2026 09:16:05 +0200 Subject: [PATCH 77/77] Update the kinds table --- src/coreclr/vm/comdelegate.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 304dad972e790c..b460e0e68bdbe9 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -2620,14 +2620,17 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT // DELEGATE KINDS TABLE // - // _target _methodPtr _methodPtrAux + // _helperObject _target _methodPtr _methodPtrAux _extraData // - // 1- Instance closed 'this' ptr target method null - // 2- Instance open non-virt delegate shuffle thunk target method - // 3- Instance open virtual delegate shuffle thunk Virtual call stub - // 4- Static closed first arg target method null - // 5- Static closed (retbuf) first arg ThisPtrRetBuf precode null - // 6- Static open delegate shuffle thunk target method + // 1- Instance closed method info 'this' ptr target method null method desc cache + // 2- Instance open non-virt method info delegate shuffle thunk target method method desc cache + // 3- Instance open virtual method info delegate shuffle thunk Virtual call stub/ method desc + // with FEATURE_CACHED_INTERFACE_DISPATCH: CID_VirtualOpenDelegateDispatch + // 4- Static closed method info first arg target method null method desc cache + // 5- Static closed (retbuf) method info first arg ThisPtrRetBuf precode null method desc cache + // 6- Static open method info delegate shuffle thunk target method method desc cache + // 7- Unmanaged null delegate marshalling stub unmanaged pointer DELEGATE_MARKER_UNMANAGEDFPTR + // 8- Multicast invocation list delegate multicast stub invoke stub invocation count // // Delegate invoke arg count == target method arg count - 2, 3, 6 // Delegate invoke arg count == 1 + target method arg count - 1, 4, 5 @@ -2637,6 +2640,7 @@ MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pT // 3 - CtorVirtualDispatch // 4 - CtorClosedStatic // 5 - Retbuf static closed form (not differentiated on this fast path; see TODO below) + // 7, 8 - Not handled here // Collectible delegates use the corresponding CtorCollectible* variants. // // With collectible types, we need to fill the _helperObject field in with a value that represents the LoaderAllocator of the target method