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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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/44] 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) {