diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs index 441036959931d..8421eea29887e 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs @@ -138,11 +138,11 @@ public static Delegate CreateDelegate(RuntimeTypeHandle typeHandleForDelegate, I } // - // Helper to extract the artifact that uniquely identifies a method in the runtime mapping tables. + // Helper to extract the artifact that identifies a reflectable delegate target in the runtime mapping tables. // - public static IntPtr GetDelegateLdFtnResult(Delegate d, out RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate, out bool isOpenResolver, out bool isInterpreterEntrypoint) + public static IntPtr GetDelegateLdFtnResult(Delegate d, out RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate, out bool isOpenResolver) { - return d.GetFunctionPointer(out typeOfFirstParameterIfInstanceDelegate, out isOpenResolver, out isInterpreterEntrypoint); + return d.GetDelegateLdFtnResult(out typeOfFirstParameterIfInstanceDelegate, out isOpenResolver); } // Low level method that returns the loaded modules as array. ReadOnlySpan returning overload diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs index a0f5312c92304..e77a106b252ba 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerServices/FunctionPointerOps.cs @@ -149,5 +149,16 @@ public static unsafe bool Compare(IntPtr functionPointerA, IntPtr functionPointe return pointerDefA->MethodFunctionPointer == pointerDefB->MethodFunctionPointer; } + + public static unsafe int GetHashCode(IntPtr functionPointer) + { + if (!IsGenericMethodPointer(functionPointer)) + { + return functionPointer.GetHashCode(); + } + + GenericMethodDescriptor* pointerDef = ConvertToGenericDescriptor(functionPointer); + return pointerDef->GetHashCode(); + } } } 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 1c565a045b826..bc8c517acb8ea 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs @@ -7,6 +7,7 @@ using System.Reflection; using System.Runtime; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Runtime.Serialization; using Internal.Reflection.Augments; @@ -40,10 +41,19 @@ protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Al // New Delegate Implementation - private object m_firstParameter; - private object m_helperObject; - private nint m_extraFunctionPointerOrData; - private IntPtr m_functionPointer; + private object _firstParameter; + private object _helperObject; + private nint _extraFunctionPointerOrData; + private IntPtr _functionPointer; + + // _helperObject may point to an array of delegates if this is a multicast delegate. We use this wrapper to distinguish between + // our own array of delegates and user provided Wrapper[]. As a added benefit, this wrapper also eliminates array co-variance + // overhead for our own array of delegates. + private struct Wrapper + { + public Wrapper(Delegate value) => Value = value; + public Delegate Value; + } // WARNING: These constants are also declared in System.Private.TypeLoader\Internal\Runtime\TypeLoader\CallConverterThunk.cs // Do not change their values without updating the values in the calling convention converter component @@ -63,14 +73,8 @@ private protected virtual IntPtr GetThunk(int whichThunk) } /// - /// Used by various parts of the runtime as a replacement for Delegate.Method - /// - /// The Interop layer uses this to distinguish between different methods on a - /// single type, and to get the function pointer for delegates to static functions - /// /// The reflection apis use this api to figure out what MethodInfo is related /// to a delegate. - /// /// /// /// This value indicates which type an delegate's function pointer is associated with @@ -79,57 +83,40 @@ private protected virtual IntPtr GetThunk(int whichThunk) /// /// This value indicates if the returned pointer is an open resolver structure. /// - /// - /// Delegate points to an object array thunk (the delegate wraps a Func<object[], object> delegate). This - /// is typically a delegate pointing to the LINQ expression interpreter. - /// - /// - internal unsafe IntPtr GetFunctionPointer(out RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate, out bool isOpenResolver, out bool isInterpreterEntrypoint) + internal unsafe IntPtr GetDelegateLdFtnResult(out RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate, out bool isOpenResolver) { typeOfFirstParameterIfInstanceDelegate = default(RuntimeTypeHandle); isOpenResolver = false; - isInterpreterEntrypoint = false; - if (GetThunk(MulticastThunk) == m_functionPointer) - { - return IntPtr.Zero; - } - else if (GetThunk(ObjectArrayThunk) == m_functionPointer) + if (_extraFunctionPointerOrData != 0) { - isInterpreterEntrypoint = true; - return IntPtr.Zero; - } - else if (m_extraFunctionPointerOrData != 0) - { - if (GetThunk(OpenInstanceThunk) == m_functionPointer) + if (GetThunk(OpenInstanceThunk) == _functionPointer) { - typeOfFirstParameterIfInstanceDelegate = ((OpenMethodResolver*)m_extraFunctionPointerOrData)->DeclaringType; + typeOfFirstParameterIfInstanceDelegate = ((OpenMethodResolver*)_extraFunctionPointerOrData)->DeclaringType; isOpenResolver = true; } - return m_extraFunctionPointerOrData; + return _extraFunctionPointerOrData; } else { - if (m_firstParameter != null) - typeOfFirstParameterIfInstanceDelegate = new RuntimeTypeHandle(m_firstParameter.GetMethodTable()); - - // TODO! Implementation issue for generic invokes here ... we need another IntPtr for uniqueness. + if (_firstParameter != null) + typeOfFirstParameterIfInstanceDelegate = new RuntimeTypeHandle(_firstParameter.GetMethodTable()); - return m_functionPointer; + return _functionPointer; } } - // This function is known to the IL Transformer. + // This function is known to the compiler. private void InitializeClosedInstance(object firstParameter, IntPtr functionPointer) { if (firstParameter is null) throw new ArgumentException(SR.Arg_DlgtNullInst); - m_functionPointer = functionPointer; - m_firstParameter = firstParameter; + _functionPointer = functionPointer; + _firstParameter = firstParameter; } - // This function is known to the IL Transformer. + // This function is known to the compiler. private void InitializeClosedInstanceSlow(object firstParameter, IntPtr functionPointer) { // This method is like InitializeClosedInstance, but it handles ALL cases. In particular, it handles generic method with fun function pointers. @@ -139,15 +126,15 @@ private void InitializeClosedInstanceSlow(object firstParameter, IntPtr function if (!FunctionPointerOps.IsGenericMethodPointer(functionPointer)) { - m_functionPointer = functionPointer; - m_firstParameter = firstParameter; + _functionPointer = functionPointer; + _firstParameter = firstParameter; } else { - m_firstParameter = this; - m_functionPointer = GetThunk(ClosedInstanceThunkOverGenericMethod); - m_extraFunctionPointerOrData = functionPointer; - m_helperObject = firstParameter; + _firstParameter = this; + _functionPointer = GetThunk(ClosedInstanceThunkOverGenericMethod); + _extraFunctionPointerOrData = functionPointer; + _helperObject = firstParameter; } } @@ -166,27 +153,28 @@ private void InitializeClosedInstanceWithGVMResolution(object firstParameter, Ru } if (!FunctionPointerOps.IsGenericMethodPointer(functionResolution)) { - m_functionPointer = functionResolution; - m_firstParameter = firstParameter; + _functionPointer = functionResolution; + _firstParameter = firstParameter; } else { - m_firstParameter = this; - m_functionPointer = GetThunk(ClosedInstanceThunkOverGenericMethod); - m_extraFunctionPointerOrData = functionResolution; - m_helperObject = firstParameter; + _firstParameter = this; + _functionPointer = GetThunk(ClosedInstanceThunkOverGenericMethod); + _extraFunctionPointerOrData = functionResolution; + _helperObject = firstParameter; } return; } + // This function is known to the compiler. private void InitializeClosedInstanceToInterface(object firstParameter, IntPtr dispatchCell) { if (firstParameter is null) throw new NullReferenceException(); - m_functionPointer = RuntimeImports.RhpResolveInterfaceMethod(firstParameter, dispatchCell); - m_firstParameter = firstParameter; + _functionPointer = RuntimeImports.RhpResolveInterfaceMethod(firstParameter, dispatchCell); + _firstParameter = firstParameter; } // This is used to implement MethodInfo.CreateDelegate() in a desktop-compatible way. Yes, the desktop really @@ -195,61 +183,53 @@ private void InitializeClosedInstanceWithoutNullCheck(object firstParameter, Int { if (!FunctionPointerOps.IsGenericMethodPointer(functionPointer)) { - m_functionPointer = functionPointer; - m_firstParameter = firstParameter; + _functionPointer = functionPointer; + _firstParameter = firstParameter; } else { - m_firstParameter = this; - m_functionPointer = GetThunk(ClosedInstanceThunkOverGenericMethod); - m_extraFunctionPointerOrData = functionPointer; - m_helperObject = firstParameter; + _firstParameter = this; + _functionPointer = GetThunk(ClosedInstanceThunkOverGenericMethod); + _extraFunctionPointerOrData = functionPointer; + _helperObject = firstParameter; } } - // This function is known to the compiler backend. + // This function is known to the compiler. private void InitializeClosedStaticThunk(object firstParameter, IntPtr functionPointer, IntPtr functionPointerThunk) { - m_extraFunctionPointerOrData = functionPointer; - m_helperObject = firstParameter; - m_functionPointer = functionPointerThunk; - m_firstParameter = this; + _extraFunctionPointerOrData = functionPointer; + _helperObject = firstParameter; + _functionPointer = functionPointerThunk; + _firstParameter = this; } - // This function is known to the compiler backend. + // This function is known to the compiler. private void InitializeOpenStaticThunk(object _ /*firstParameter*/, IntPtr functionPointer, IntPtr functionPointerThunk) { // This sort of delegate is invoked by calling the thunk function pointer with the arguments to the delegate + a reference to the delegate object itself. - m_firstParameter = this; - m_functionPointer = functionPointerThunk; - m_extraFunctionPointerOrData = functionPointer; + _firstParameter = this; + _functionPointer = functionPointerThunk; + _extraFunctionPointerOrData = functionPointer; } private void InitializeOpenInstanceThunkDynamic(IntPtr functionPointer, IntPtr functionPointerThunk) { // This sort of delegate is invoked by calling the thunk function pointer with the arguments to the delegate + a reference to the delegate object itself. - m_firstParameter = this; - m_functionPointer = functionPointerThunk; - m_extraFunctionPointerOrData = functionPointer; + _firstParameter = this; + _functionPointer = functionPointerThunk; + _extraFunctionPointerOrData = functionPointer; } // This function is only ever called by the open instance method thunk, and in that case, - // m_extraFunctionPointerOrData always points to an OpenMethodResolver + // _extraFunctionPointerOrData always points to an OpenMethodResolver [MethodImpl(MethodImplOptions.NoInlining)] private IntPtr GetActualTargetFunctionPointer(object thisObject) { - return OpenMethodResolver.ResolveMethod(m_extraFunctionPointerOrData, thisObject); + return OpenMethodResolver.ResolveMethod(_extraFunctionPointerOrData, thisObject); } - internal bool IsDynamicDelegate() - { - if (this.GetThunk(MulticastThunk) == IntPtr.Zero) - { - return true; - } - - return false; - } + internal bool IsDynamicDelegate() => GetThunk(MulticastThunk) == IntPtr.Zero; [DebuggerGuidedStepThroughAttribute] protected virtual object? DynamicInvokeImpl(object?[]? args) @@ -257,7 +237,7 @@ internal bool IsDynamicDelegate() if (IsDynamicDelegate()) { // DynamicDelegate case - object? result = ((Func)m_helperObject)(args); + object? result = ((Func)_helperObject)(args); DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); return result; } @@ -265,7 +245,7 @@ internal bool IsDynamicDelegate() { DynamicInvokeInfo dynamicInvokeInfo = ReflectionAugments.ReflectionCoreCallbacks.GetDelegateDynamicInvokeInfo(GetType()); - object? result = dynamicInvokeInfo.Invoke(m_firstParameter, m_functionPointer, + object? result = dynamicInvokeInfo.Invoke(_firstParameter, _functionPointer, args, binderBundle: null, wrapInTargetInvocationException: true); DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); return result; @@ -274,35 +254,53 @@ internal bool IsDynamicDelegate() protected virtual MethodInfo GetMethodImpl() { + // Multi-cast delegates return the Method of the last delegate in the list + if (_helperObject is Wrapper[] invocationList) + { + int invocationCount = (int)_extraFunctionPointerOrData; + return invocationList[invocationCount - 1].Value.GetMethodImpl(); + } + + // Return the delegate Invoke method for marshalled function pointers and LINQ expressions + if ((_firstParameter is NativeFunctionPointerWrapper) || (_functionPointer == GetThunk(ObjectArrayThunk))) + { + return GetType().GetMethod("Invoke"); + } + return ReflectionAugments.ReflectionCoreCallbacks.GetDelegateMethod(this); } - public object Target + public object? Target { get { // Multi-cast delegates return the Target of the last delegate in the list - if (m_functionPointer == GetThunk(MulticastThunk)) + if (_helperObject is Wrapper[] invocationList) { - Delegate[] invocationList = (Delegate[])m_helperObject; - int invocationCount = (int)m_extraFunctionPointerOrData; - return invocationList[invocationCount - 1].Target; + int invocationCount = (int)_extraFunctionPointerOrData; + return invocationList[invocationCount - 1].Value.Target; } - // Closed static delegates place a value in m_helperObject that they pass to the target method. - if (m_functionPointer == GetThunk(ClosedStaticThunk) || - m_functionPointer == GetThunk(ClosedInstanceThunkOverGenericMethod) || - m_functionPointer == GetThunk(ObjectArrayThunk)) - return m_helperObject; + // Closed static delegates place a value in _helperObject that they pass to the target method. + if (_functionPointer == GetThunk(ClosedStaticThunk) || + _functionPointer == GetThunk(ClosedInstanceThunkOverGenericMethod) || + _functionPointer == GetThunk(ObjectArrayThunk)) + return _helperObject; + + // Other non-closed thunks can be identified as the _firstParameter field points at this. + if (object.ReferenceEquals(_firstParameter, this)) + { + return null; + } - // Other non-closed thunks can be identified as the m_firstParameter field points at this. - if (object.ReferenceEquals(m_firstParameter, this)) + // NativeFunctionPointerWrapper used by marshalled function pointers is not returned as a public target + if (_firstParameter is NativeFunctionPointerWrapper) { return null; } - // Closed instance delegates place a value in m_firstParameter, and we've ruled out all other types of delegates - return m_firstParameter; + // Closed instance delegates place a value in _firstParameter, and we've ruled out all other types of delegates + return _firstParameter; } } @@ -319,13 +317,9 @@ public object Target // V1 api: Creates open delegates to static methods only, relaxed signature checking disallowed. public static Delegate CreateDelegate(Type type, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type target, string method, bool ignoreCase, bool throwOnBindFailure) => ReflectionAugments.ReflectionCoreCallbacks.CreateDelegate(type, target, method, ignoreCase, throwOnBindFailure); - internal bool IsOpenStatic - { - get - { - return GetThunk(OpenStaticThunk) == m_functionPointer; - } - } + internal IntPtr TryGetOpenStaticFunctionPointer() => (GetThunk(OpenStaticThunk) == _functionPointer) ? _extraFunctionPointerOrData : 0; + + internal NativeFunctionPointerWrapper? TryGetNativeFunctionPointerWrapper() => _firstParameter as NativeFunctionPointerWrapper; internal static unsafe bool InternalEqualTypes(object a, object b) { @@ -350,9 +344,9 @@ internal static unsafe Delegate CreateObjectArrayDelegate(Type t, Func(RuntimeImports.RhNewObject(this.GetMethodTable())); // 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 - if (thisIsMultiCastAlready) - { - result.m_functionPointer = this.m_functionPointer; - } - else - { - result.m_functionPointer = GetThunk(MulticastThunk); - } - result.m_firstParameter = result; - result.m_helperObject = invocationList; - result.m_extraFunctionPointerOrData = (IntPtr)invocationCount; + // copy _functionPointer field rather than calling GetThunk to get it + result._functionPointer = thisIsMultiCastAlready ? _functionPointer : GetThunk(MulticastThunk); + result._firstParameter = result; + result._helperObject = invocationList; + result._extraFunctionPointerOrData = (IntPtr)invocationCount; return result; } - private static bool TrySetSlot(Delegate[] a, int index, Delegate o) + private static bool TrySetSlot(Wrapper[] a, int index, Delegate o) { - if (a[index] == null && System.Threading.Interlocked.CompareExchange(ref a[index], o, null) == null) + if (a[index].Value == null && System.Threading.Interlocked.CompareExchange(ref a[index].Value, 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] != null) + if (a[index].Value is Delegate dd) { - MulticastDelegate d = (MulticastDelegate)o; - MulticastDelegate dd = (MulticastDelegate)a[index]; - - if (object.ReferenceEquals(dd.m_firstParameter, d.m_firstParameter) && - object.ReferenceEquals(dd.m_helperObject, d.m_helperObject) && - dd.m_extraFunctionPointerOrData == d.m_extraFunctionPointerOrData && - dd.m_functionPointer == d.m_functionPointer) + if (object.ReferenceEquals(dd._firstParameter, o._firstParameter) && + object.ReferenceEquals(dd._helperObject, o._helperObject) && + dd._extraFunctionPointerOrData == o._extraFunctionPointerOrData && + dd._functionPointer == o._functionPointer) { return true; } @@ -446,35 +430,31 @@ private static bool TrySetSlot(Delegate[] a, int index, Delegate o) // to form a new delegate. protected virtual Delegate CombineImpl(Delegate? d) { - if (d is null) // cast to object for a more efficient test + if (d is null) return this; // Verify that the types are the same... if (!InternalEqualTypes(this, d)) throw new ArgumentException(SR.Arg_DlgtTypeMis); - if (IsDynamicDelegate() && d.IsDynamicDelegate()) - { + if (IsDynamicDelegate()) throw new InvalidOperationException(); - } - MulticastDelegate dFollow = (MulticastDelegate)d; - Delegate[]? resultList; int followCount = 1; - Delegate[]? followList = dFollow.m_helperObject as Delegate[]; + Wrapper[]? followList = d._helperObject as Wrapper[]; if (followList != null) - followCount = (int)dFollow.m_extraFunctionPointerOrData; + followCount = (int)d._extraFunctionPointerOrData; int resultCount; - Delegate[]? invocationList = m_helperObject as Delegate[]; - if (invocationList == null) + Wrapper[]? resultList; + if (_helperObject is not Wrapper[] invocationList) { resultCount = 1 + followCount; - resultList = new Delegate[resultCount]; - resultList[0] = this; + resultList = new Wrapper[resultCount]; + resultList[0] = new Wrapper(this); if (followList == null) { - resultList[1] = dFollow; + resultList[1] = new Wrapper(d); } else { @@ -485,7 +465,7 @@ protected virtual Delegate CombineImpl(Delegate? d) } else { - int invocationCount = (int)m_extraFunctionPointerOrData; + int invocationCount = (int)_extraFunctionPointerOrData; resultCount = invocationCount + followCount; resultList = null; if (resultCount <= invocationList.Length) @@ -493,14 +473,14 @@ protected virtual Delegate CombineImpl(Delegate? d) resultList = invocationList; if (followList == null) { - if (!TrySetSlot(resultList, invocationCount, dFollow)) + if (!TrySetSlot(resultList, invocationCount, d)) resultList = null; } else { for (int i = 0; i < followCount; i++) { - if (!TrySetSlot(resultList, invocationCount + i, followList[i])) + if (!TrySetSlot(resultList, invocationCount + i, followList[i].Value)) { resultList = null; break; @@ -515,14 +495,14 @@ protected virtual Delegate CombineImpl(Delegate? d) while (allocCount < resultCount) allocCount *= 2; - resultList = new Delegate[allocCount]; + resultList = new Wrapper[allocCount]; for (int i = 0; i < invocationCount; i++) resultList[i] = invocationList[i]; if (followList == null) { - resultList[invocationCount] = dFollow; + resultList[invocationCount] = new Wrapper(d); } else { @@ -534,14 +514,13 @@ protected virtual Delegate CombineImpl(Delegate? d) } } - private Delegate[] DeleteFromInvocationList(Delegate[] invocationList, int invocationCount, int deleteIndex, int deleteCount) + private static Wrapper[] DeleteFromInvocationList(Wrapper[] invocationList, int invocationCount, int deleteIndex, int deleteCount) { - Delegate[] thisInvocationList = (Delegate[])m_helperObject; - int allocCount = thisInvocationList.Length; + int allocCount = invocationList.Length; while (allocCount / 2 >= invocationCount - deleteCount) allocCount /= 2; - Delegate[] newInvocationList = new Delegate[allocCount]; + Wrapper[] newInvocationList = new Wrapper[allocCount]; for (int i = 0; i < deleteIndex; i++) newInvocationList[i] = invocationList[i]; @@ -552,11 +531,11 @@ private Delegate[] DeleteFromInvocationList(Delegate[] invocationList, int invoc return newInvocationList; } - private static bool EqualInvocationLists(Delegate[] a, Delegate[] b, int start, int count) + private static bool EqualInvocationLists(Wrapper[] a, Wrapper[] b, int start, int count) { for (int i = 0; i < count; i++) { - if (!(a[start + i].Equals(b[i]))) + if (!(a[start + i].Value.Equals(b[i].Value))) return false; } return true; @@ -572,34 +551,31 @@ private static bool EqualInvocationLists(Delegate[] a, Delegate[] b, int start, // There is a special case were we are removing using a delegate as // the value we need to check for this case // - MulticastDelegate? v = d as MulticastDelegate; - - if (v is null) + if (d is null) return this; - if (v.m_helperObject as Delegate[] == null) + if (d._helperObject is not Wrapper[] dInvocationList) { - Delegate[]? invocationList = m_helperObject as Delegate[]; - if (invocationList == null) + if (_helperObject is not Wrapper[] invocationList) { // they are both not real Multicast - if (this.Equals(v)) + if (this.Equals(d)) return null; } else { - int invocationCount = (int)m_extraFunctionPointerOrData; + int invocationCount = (int)_extraFunctionPointerOrData; for (int i = invocationCount; --i >= 0;) { - if (v.Equals(invocationList[i])) + if (d.Equals(invocationList[i].Value)) { if (invocationCount == 2) { // Special case - only one value left, either at the beginning or the end - return invocationList[1 - i]; + return invocationList[1 - i].Value; } else { - Delegate[] list = DeleteFromInvocationList(invocationList, invocationCount, i, 1); + Wrapper[] list = DeleteFromInvocationList(invocationList, invocationCount, i, 1); return NewMulticastDelegate(list, invocationCount - 1, true); } } @@ -608,29 +584,28 @@ private static bool EqualInvocationLists(Delegate[] a, Delegate[] b, int start, } else { - Delegate[]? invocationList = m_helperObject as Delegate[]; - if (invocationList != null) + if (_helperObject is Wrapper[] invocationList) { - int invocationCount = (int)m_extraFunctionPointerOrData; - int vInvocationCount = (int)v.m_extraFunctionPointerOrData; - for (int i = invocationCount - vInvocationCount; i >= 0; i--) + int invocationCount = (int)_extraFunctionPointerOrData; + int dInvocationCount = (int)d._extraFunctionPointerOrData; + for (int i = invocationCount - dInvocationCount; i >= 0; i--) { - if (EqualInvocationLists(invocationList, v.m_helperObject as Delegate[], i, vInvocationCount)) + if (EqualInvocationLists(invocationList, dInvocationList, i, dInvocationCount)) { - if (invocationCount - vInvocationCount == 0) + if (invocationCount - dInvocationCount == 0) { // Special case - no values left return null; } - else if (invocationCount - vInvocationCount == 1) + else if (invocationCount - dInvocationCount == 1) { // Special case - only one value left, either at the beginning or the end - return invocationList[i != 0 ? 0 : invocationCount - 1]; + return invocationList[i != 0 ? 0 : invocationCount - 1].Value; } else { - Delegate[] list = DeleteFromInvocationList(invocationList, invocationCount, i, vInvocationCount); - return NewMulticastDelegate(list, invocationCount - vInvocationCount, true); + Wrapper[] list = DeleteFromInvocationList(invocationList, invocationCount, i, dInvocationCount); + return NewMulticastDelegate(list, invocationCount - dInvocationCount, true); } } } @@ -642,41 +617,19 @@ private static bool EqualInvocationLists(Delegate[] a, Delegate[] b, int start, public virtual Delegate[] GetInvocationList() { - Delegate[] del; - Delegate[]? invocationList = m_helperObject as Delegate[]; - if (invocationList == null) - { - del = new Delegate[1]; - del[0] = this; - } - else + if (_helperObject is Wrapper[] invocationList) { // Create an array of delegate copies and each // element into the array - int invocationCount = (int)m_extraFunctionPointerOrData; - del = new Delegate[invocationCount]; + int invocationCount = (int)_extraFunctionPointerOrData; + var del = new Delegate[invocationCount]; for (int i = 0; i < del.Length; i++) - del[i] = invocationList[i]; + del[i] = invocationList[i].Value; + return del; } - return del; - } - - private bool InvocationListEquals(MulticastDelegate d) - { - Delegate[] invocationList = (Delegate[])m_helperObject; - if (d.m_extraFunctionPointerOrData != m_extraFunctionPointerOrData) - return false; - int invocationCount = (int)m_extraFunctionPointerOrData; - for (int i = 0; i < invocationCount; i++) - { - Delegate dd = invocationList[i]; - Delegate[] dInvocationList = (Delegate[])d.m_helperObject; - if (!dd.Equals(dInvocationList[i])) - return false; - } - return true; + return new Delegate[] { this }; } public override bool Equals([NotNullWhen(true)] object? obj) @@ -688,73 +641,92 @@ public override bool Equals([NotNullWhen(true)] object? obj) 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!"); - var d = Unsafe.As(obj); + // 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!"); + var d = Unsafe.As(obj); - // there are 2 kind of delegate kinds for comparison - // 1- Multicast (m_helperObject is Delegate[]) - // 2- Single-cast delegate, which can be compared with a structural comparison - - IntPtr multicastThunk = GetThunk(MulticastThunk); - if (m_functionPointer == multicastThunk) - { - return d.m_functionPointer == multicastThunk && InvocationListEquals(d); - } - else + if (_helperObject is Wrapper[] invocationList) { - if (!object.ReferenceEquals(m_helperObject, d.m_helperObject) || - (!FunctionPointerOps.Compare(m_extraFunctionPointerOrData, d.m_extraFunctionPointerOrData)) || - (!FunctionPointerOps.Compare(m_functionPointer, d.m_functionPointer))) - { + if (d._extraFunctionPointerOrData != _extraFunctionPointerOrData) return false; - } - // Those delegate kinds with thunks put themselves into the m_firstParameter, so we can't - // blindly compare the m_firstParameter fields for equality. - if (object.ReferenceEquals(m_firstParameter, this)) + if (d._helperObject is not Wrapper[] dInvocationList) + return false; + + int invocationCount = (int)_extraFunctionPointerOrData; + for (int i = 0; i < invocationCount; i++) { - return object.ReferenceEquals(d.m_firstParameter, d); + if (!invocationList[i].Value.Equals(dInvocationList[i].Value)) + return false; } + return true; + } - return object.ReferenceEquals(m_firstParameter, d.m_firstParameter); + if (_firstParameter is NativeFunctionPointerWrapper nativeFunctionPointerWrapper) + { + if (d._firstParameter is not NativeFunctionPointerWrapper dnativeFunctionPointerWrapper) + return false; + + return nativeFunctionPointerWrapper.NativeFunctionPointer == dnativeFunctionPointerWrapper.NativeFunctionPointer; + } + + if (!object.ReferenceEquals(_helperObject, d._helperObject) || + (!FunctionPointerOps.Compare(_extraFunctionPointerOrData, d._extraFunctionPointerOrData)) || + (!FunctionPointerOps.Compare(_functionPointer, d._functionPointer))) + { + return false; + } + + // Those delegate kinds with thunks put themselves into the _firstParameter, so we can't + // blindly compare the _firstParameter fields for equality. + if (object.ReferenceEquals(_firstParameter, this)) + { + return object.ReferenceEquals(d._firstParameter, d); } + + return object.ReferenceEquals(_firstParameter, d._firstParameter); } public override int GetHashCode() { - Delegate[]? invocationList = m_helperObject as Delegate[]; - if (invocationList == null) - { - return base.GetHashCode(); - } - else + if (_helperObject is Wrapper[] invocationList) { - int hash = 0; - for (int i = 0; i < (int)m_extraFunctionPointerOrData; i++) + int multiCastHash = 0; + for (int i = 0; i < (int)_extraFunctionPointerOrData; i++) { - hash = hash * 33 + invocationList[i].GetHashCode(); + multiCastHash = multiCastHash * 33 + invocationList[i].Value.GetHashCode(); } + return multiCastHash; + } + + if (_firstParameter is NativeFunctionPointerWrapper nativeFunctionPointerWrapper) + { + return nativeFunctionPointerWrapper.NativeFunctionPointer.GetHashCode(); + } + + int hash = RuntimeHelpers.GetHashCode(_helperObject) + + 7 * FunctionPointerOps.GetHashCode(_extraFunctionPointerOrData) + + 13 * FunctionPointerOps.GetHashCode(_functionPointer); - return hash; + if (!object.ReferenceEquals(_firstParameter, this)) + { + hash += 17 * RuntimeHelpers.GetHashCode(_firstParameter); } + + return hash; } - public bool HasSingleTarget => !(m_helperObject is Delegate[]); + public bool HasSingleTarget => _helperObject is not Wrapper[]; // Used by delegate invocation list enumerator internal Delegate? TryGetAt(int index) { - if (!(m_helperObject is Delegate[] invocationList)) + if (_helperObject is Wrapper[] invocationList) { - return (index == 0) ? this : null; - } - else - { - return ((uint)index < (uint)m_extraFunctionPointerOrData) ? invocationList[index] : null; + return ((uint)index < (uint)_extraFunctionPointerOrData) ? invocationList[index].Value : null; } + + return (index == 0) ? this : null; } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeFunctionPointerWrapper.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeFunctionPointerWrapper.cs index bb3ea2b5f10e2..4571165530abf 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeFunctionPointerWrapper.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/NativeFunctionPointerWrapper.cs @@ -12,14 +12,9 @@ internal abstract class NativeFunctionPointerWrapper { public NativeFunctionPointerWrapper(IntPtr nativeFunctionPointer) { - m_nativeFunctionPointer = nativeFunctionPointer; + NativeFunctionPointer = nativeFunctionPointer; } - private IntPtr m_nativeFunctionPointer; - - public IntPtr NativeFunctionPointer - { - get { return m_nativeFunctionPointer; } - } + public IntPtr NativeFunctionPointer { get; } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs index bf7fa122af6c5..0e1ad8d048653 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs @@ -56,7 +56,7 @@ public static unsafe IntPtr GetFunctionPointerForDelegate(Delegate del) throw new ArgumentException(SR.Argument_NeedNonGenericType, "delegate"); #pragma warning restore CA2208 - NativeFunctionPointerWrapper? fpWrapper = del.Target as NativeFunctionPointerWrapper; + NativeFunctionPointerWrapper? fpWrapper = del.TryGetNativeFunctionPointerWrapper(); if (fpWrapper != null) { // @@ -104,64 +104,71 @@ internal unsafe struct ThunkContextData public IntPtr FunctionPtr; // Function pointer for open static delegates } - internal sealed class PInvokeDelegateThunk + internal sealed unsafe class PInvokeDelegateThunk { - public IntPtr Thunk; // Thunk pointer - public IntPtr ContextData; // ThunkContextData pointer which will be stored in the context slot of the thunk + public readonly IntPtr Thunk; // Thunk pointer + public readonly IntPtr ContextData; // ThunkContextData pointer which will be stored in the context slot of the thunk public PInvokeDelegateThunk(Delegate del) { - Thunk = RuntimeAugments.AllocateThunk(s_thunkPoolHeap); - Debug.Assert(Thunk != IntPtr.Zero); - if (Thunk == IntPtr.Zero) { - // We've either run out of memory, or failed to allocate a new thunk due to some other bug. Now we should fail fast - Environment.FailFast("Insufficient number of thunks."); + throw new OutOfMemoryException(); } - else - { - // - // Allocate unmanaged memory for GCHandle of delegate and function pointer of open static delegate - // We will store this pointer on the context slot of thunk data - // - unsafe - { - ContextData = (IntPtr)NativeMemory.Alloc((nuint)(2 * IntPtr.Size)); - ThunkContextData* thunkData = (ThunkContextData*)ContextData; + // + // For open static delegates set target to ReverseOpenStaticDelegateStub which calls the static function pointer directly + // + IntPtr openStaticFunctionPointer = del.TryGetOpenStaticFunctionPointer(); + + // + // Allocate unmanaged memory for GCHandle of delegate and function pointer of open static delegate + // We will store this pointer on the context slot of thunk data + // + unsafe + { + ContextData = (IntPtr)NativeMemory.AllocZeroed((nuint)(2 * IntPtr.Size)); - // allocate a weak GChandle for the delegate - thunkData->Handle = GCHandle.Alloc(del, GCHandleType.Weak); + ThunkContextData* thunkData = (ThunkContextData*)ContextData; - // if it is an open static delegate get the function pointer - if (del.IsOpenStatic) - thunkData->FunctionPtr = del.GetFunctionPointer(out RuntimeTypeHandle _, out bool _, out bool _); - else - thunkData->FunctionPtr = default; - } + // allocate a weak GChandle for the delegate + thunkData->Handle = GCHandle.Alloc(del, GCHandleType.WeakTrackResurrection); + thunkData->FunctionPtr = openStaticFunctionPointer; } + + IntPtr pTarget = RuntimeInteropData.GetDelegateMarshallingStub(new RuntimeTypeHandle(del.GetMethodTable()), openStaticFunctionPointer != IntPtr.Zero); + Debug.Assert(pTarget != IntPtr.Zero); + + RuntimeAugments.SetThunkData(s_thunkPoolHeap, Thunk, ContextData, pTarget); } ~PInvokeDelegateThunk() { - // Free the thunk - RuntimeAugments.FreeThunk(s_thunkPoolHeap, Thunk); - unsafe + if (ContextData != IntPtr.Zero) { - if (ContextData != IntPtr.Zero) + // free the GCHandle + GCHandle handle = ((ThunkContextData*)ContextData)->Handle; + if (handle.IsAllocated) { - // free the GCHandle - GCHandle handle = ((ThunkContextData*)ContextData)->Handle; - if (handle.IsAllocated) + // If the delegate is still alive, defer finalization. + if (handle.Target != null) { - handle.Free(); + GC.ReRegisterForFinalize(this); + return; } - // Free the allocated context data memory - NativeMemory.Free((void*)ContextData); + handle.Free(); } + + // Free the allocated context data memory + NativeMemory.Free((void*)ContextData); + } + + // Free the thunk + if (Thunk != IntPtr.Zero) + { + RuntimeAugments.FreeThunk(s_thunkPoolHeap, Thunk); } } } @@ -179,19 +186,7 @@ private static unsafe PInvokeDelegateThunk AllocateThunk(Delegate del) Debug.Assert(s_thunkPoolHeap != null); } - var delegateThunk = new PInvokeDelegateThunk(del); - - // - // For open static delegates set target to ReverseOpenStaticDelegateStub which calls the static function pointer directly - // - bool openStaticDelegate = del.IsOpenStatic; - - IntPtr pTarget = RuntimeInteropData.GetDelegateMarshallingStub(new RuntimeTypeHandle(del.GetMethodTable()), openStaticDelegate); - Debug.Assert(pTarget != IntPtr.Zero); - - RuntimeAugments.SetThunkData(s_thunkPoolHeap, delegateThunk.Thunk, delegateThunk.ContextData, pTarget); - - return delegateThunk; + return new PInvokeDelegateThunk(del); } /// diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Extensions/NonPortable/DelegateMethodInfoRetriever.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Extensions/NonPortable/DelegateMethodInfoRetriever.cs index 5ae75d1d55eb9..5669203a6f39d 100644 --- a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Extensions/NonPortable/DelegateMethodInfoRetriever.cs +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Extensions/NonPortable/DelegateMethodInfoRetriever.cs @@ -17,20 +17,7 @@ public static class DelegateMethodInfoRetriever { public static MethodInfo GetDelegateMethodInfo(Delegate del) { - Delegate[] invokeList = del.GetInvocationList(); - del = invokeList[invokeList.Length - 1]; - IntPtr originalLdFtnResult = RuntimeAugments.GetDelegateLdFtnResult(del, out RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate, out bool isOpenResolver, out bool isInterpreterEntrypoint); - - if (isInterpreterEntrypoint) - { - // This is a special kind of delegate where the invoke method is "ObjectArrayThunk". Typically, - // this will be a delegate that points the LINQ Expression interpreter. We could manufacture - // a MethodInfo based on the delegate's Invoke signature, but let's just throw for now. - throw new NotSupportedException(SR.DelegateGetMethodInfo_ObjectArrayDelegate); - } - - if (originalLdFtnResult == (IntPtr)0) - return null; + IntPtr originalLdFtnResult = RuntimeAugments.GetDelegateLdFtnResult(del, out RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate, out bool isOpenResolver); QMethodDefinition methodHandle = default(QMethodDefinition); RuntimeTypeHandle[] genericMethodTypeArgumentHandles = null; @@ -79,11 +66,7 @@ public static MethodInfo GetDelegateMethodInfo(Delegate del) throw new NotSupportedException(SR.Format(SR.DelegateGetMethodInfo_NoDynamic_WithDisplayString, methodDisplayString)); } } - MethodBase methodBase = ExecutionDomain.GetMethod(typeOfFirstParameterIfInstanceDelegate, methodHandle, genericMethodTypeArgumentHandles); - MethodInfo methodInfo = methodBase as MethodInfo; - if (methodInfo != null) - return methodInfo; - return null; // GetMethod() returned a ConstructorInfo. + return (MethodInfo)ExecutionDomain.GetMethod(typeOfFirstParameterIfInstanceDelegate, methodHandle, genericMethodTypeArgumentHandles); } } } diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Resources/Strings.resx b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Resources/Strings.resx index 80023f62d226a..e5432845a5206 100644 --- a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Resources/Strings.resx +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Resources/Strings.resx @@ -204,9 +204,6 @@ Cannot retrieve a MethodInfo for this delegate because the necessary generic instantiation was not metadata-enabled. - - Cannot retrieve a MethodInfo for this delegate because the delegate target is an interpreted LINQ expression. - Could not retrieve the mapping of the interface '{0}' on type '{1}' because the type implements the interface abstractly. diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index fdaae7fdb715b..53779e45d4121 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3285,7 +3285,7 @@ private void getEEInfo(ref CORINFO_EE_INFO pEEInfoOut) pEEInfoOut.inlinedCallFrameInfo.size = (uint)SizeOfPInvokeTransitionFrame; - pEEInfoOut.offsetOfDelegateInstance = (uint)pointerSize; // Delegate::m_firstParameter + pEEInfoOut.offsetOfDelegateInstance = (uint)pointerSize; // Delegate::_firstParameter pEEInfoOut.offsetOfDelegateFirstTarget = OffsetOfDelegateFirstTarget; pEEInfoOut.sizeOfReversePInvokeFrame = (uint)SizeOfReversePInvokeTransitionFrame; diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateMethodILEmitter.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateMethodILEmitter.cs index 905d40fd2828e..baa2ff1f354b0 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateMethodILEmitter.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateMethodILEmitter.cs @@ -46,8 +46,8 @@ public static MethodIL EmitIL(MethodDesc method) ILEmitter emit = new ILEmitter(); TypeDesc delegateType = context.GetWellKnownType(WellKnownType.MulticastDelegate).BaseType; - FieldDesc firstParameterField = delegateType.GetKnownField("m_firstParameter"); - FieldDesc functionPointerField = delegateType.GetKnownField("m_functionPointer"); + FieldDesc firstParameterField = delegateType.GetKnownField("_firstParameter"); + FieldDesc functionPointerField = delegateType.GetKnownField("_functionPointer"); ILCodeStream codeStream = emit.NewCodeStream(); codeStream.EmitLdArg(0); diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateThunks.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateThunks.cs index 99834d7cb2363..3845514eb4db0 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateThunks.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DelegateThunks.cs @@ -63,7 +63,7 @@ protected FieldDesc ExtraFunctionPointerOrDataField { get { - return SystemDelegateType.GetKnownField("m_extraFunctionPointerOrData"); + return SystemDelegateType.GetKnownField("_extraFunctionPointerOrData"); } } @@ -71,7 +71,7 @@ protected FieldDesc HelperObjectField { get { - return SystemDelegateType.GetKnownField("m_helperObject"); + return SystemDelegateType.GetKnownField("_helperObject"); } } @@ -79,7 +79,7 @@ protected FieldDesc FirstParameterField { get { - return SystemDelegateType.GetKnownField("m_firstParameter"); + return SystemDelegateType.GetKnownField("_firstParameter"); } } @@ -87,7 +87,7 @@ protected FieldDesc FunctionPointerField { get { - return SystemDelegateType.GetKnownField("m_functionPointer"); + return SystemDelegateType.GetKnownField("_functionPointer"); } } @@ -304,7 +304,8 @@ public override MethodIL EmitIL() ILEmitter emitter = new ILEmitter(); ILCodeStream codeStream = emitter.NewCodeStream(); - ArrayType invocationListArrayType = SystemDelegateType.MakeArrayType(); + TypeDesc delegateWrapperType = ((MetadataType)SystemDelegateType).GetKnownNestedType("Wrapper"); + ArrayType invocationListArrayType = delegateWrapperType.MakeArrayType(); ILLocalVariable delegateArrayLocal = emitter.NewLocal(invocationListArrayType); ILLocalVariable invocationCountLocal = emitter.NewLocal(Context.GetWellKnownType(WellKnownType.Int32)); @@ -318,11 +319,11 @@ public override MethodIL EmitIL() } // Fill in delegateArrayLocal - // Delegate[] delegateArrayLocal = (Delegate[])this.m_helperObject + // Delegate.Wrapper[] delegateArrayLocal = (Delegate.Wrapper[])this._helperObject // ldarg.0 (this pointer) - // ldfld Delegate.HelperObjectField - // castclass Delegate[] + // ldfld Delegate._helperObject + // castclass Delegate.Wrapper[] // stloc delegateArrayLocal codeStream.EmitLdArg(0); codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(HelperObjectField)); @@ -330,9 +331,10 @@ public override MethodIL EmitIL() codeStream.EmitStLoc(delegateArrayLocal); // Fill in invocationCountLocal - // int invocationCountLocal = this.m_extraFunctionPointerOrData + // int invocationCountLocal = this._extraFunctionPointerOrData + // ldarg.0 (this pointer) - // ldfld Delegate.m_extraFunctionPointerOrData + // ldfld Delegate._extraFunctionPointerOrData // stloc invocationCountLocal codeStream.EmitLdArg(0); codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(ExtraFunctionPointerOrDataField)); @@ -352,25 +354,27 @@ public override MethodIL EmitIL() // Implement as do/while loop. We only have this stub in play if we're in the multicast situation // Find the delegate to call - // Delegate = delegateToCallLocal = delegateArrayLocal[iteratorLocal]; + // Delegate = delegateToCallLocal = delegateArrayLocal[iteratorLocal].Value; // ldloc delegateArrayLocal // ldloc iteratorLocal - // ldelem System.Delegate + // ldelema Delegate.Wrapper + // ldfld Delegate.Wrapper.Value // stloc delegateToCallLocal codeStream.EmitLdLoc(delegateArrayLocal); codeStream.EmitLdLoc(iteratorLocal); - codeStream.Emit(ILOpcode.ldelem, emitter.NewToken(SystemDelegateType)); + codeStream.Emit(ILOpcode.ldelema, emitter.NewToken(delegateWrapperType)); + codeStream.Emit(ILOpcode.ldfld, emitter.NewToken(delegateWrapperType.GetKnownField("Value"))); codeStream.EmitStLoc(delegateToCallLocal); // Call the delegate // returnValueLocal = delegateToCallLocal(...); // ldloc delegateToCallLocal - // ldfld System.Delegate.m_firstParameter + // ldfld Delegate._firstParameter // ldarg 1, n // ldloc delegateToCallLocal - // ldfld System.Delegate.m_functionPointer + // ldfld Delegate._functionPointer // calli returnValueType thiscall (all the params) // IF there is a return value // stloc returnValueLocal @@ -501,7 +505,7 @@ public override MethodIL EmitIL() // args[1] = param1; // ... // try { - // ret = ((Func)dlg.m_helperObject)(args); + // ret = ((Func)dlg._helperObject)(args); // } finally { // param0 = (T0)args[0]; // only generated for each byref argument // } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs index 9d594b41bbc99..19d8ee6efd959 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/TypePreinit.cs @@ -2797,16 +2797,16 @@ public void WriteContent(ref ObjectDataBuilder builder, ISymbolNode thisNode, No { Debug.Assert(creationInfo.Constructor.Method.Name == "InitializeOpenStaticThunk"); - // m_firstParameter + // _firstParameter builder.EmitPointerReloc(thisNode); - // m_helperObject + // _helperObject builder.EmitZeroPointer(); - // m_extraFunctionPointerOrData + // _extraFunctionPointerOrData builder.EmitPointerReloc(creationInfo.GetTargetNode(factory)); - // m_functionPointer + // _functionPointer Debug.Assert(creationInfo.Thunk != null); builder.EmitPointerReloc(creationInfo.Thunk); } @@ -2814,16 +2814,16 @@ public void WriteContent(ref ObjectDataBuilder builder, ISymbolNode thisNode, No { Debug.Assert(creationInfo.Constructor.Method.Name == "InitializeClosedInstance"); - // m_firstParameter + // _firstParameter _firstParameter.WriteFieldData(ref builder, factory); - // m_helperObject + // _helperObject builder.EmitZeroPointer(); - // m_extraFunctionPointerOrData + // _extraFunctionPointerOrData builder.EmitZeroPointer(); - // m_functionPointer + // _functionPointer builder.EmitPointerReloc(factory.CanonicalEntrypoint(_methodPointed)); } } 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 ad83b1eb42a5d..96911a9289804 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)(3 * PointerSize); // Delegate._methodPtr private readonly ReadyToRunCodegenCompilation _compilation; private MethodWithGCInfo _methodCodeNode; diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs index 1d2f977c4c167..b482a153539af 100644 --- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs +++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs @@ -28,7 +28,7 @@ internal unsafe partial class CorInfoImpl { private const CORINFO_RUNTIME_ABI TargetABI = CORINFO_RUNTIME_ABI.CORINFO_NATIVEAOT_ABI; - private uint OffsetOfDelegateFirstTarget => (uint)(4 * PointerSize); // Delegate::m_functionPointer + private uint OffsetOfDelegateFirstTarget => (uint)(4 * PointerSize); // Delegate._functionPointer private int SizeOfReversePInvokeTransitionFrame => 2 * PointerSize; private RyuJitCompilation _compilation; diff --git a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/Marshal/GetDelegateForFunctionPointerTests.cs b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/Marshal/GetDelegateForFunctionPointerTests.cs index abcf514822786..f0745272af906 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/Marshal/GetDelegateForFunctionPointerTests.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/System.Runtime.InteropServices.UnitTests/System/Runtime/InteropServices/Marshal/GetDelegateForFunctionPointerTests.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices.Tests.Common; using Xunit; @@ -153,6 +154,66 @@ public void GetDelegateForFunctionPointer_CantCast_ThrowsInvalidCastException() GC.KeepAlive(d); } + [Fact] + [ActiveIssue("https://github.com/dotnet/runtime/issues/99478", TestRuntimes.Mono)] + public void GetDelegateForFunctionPointer_Resurrection() + { + GCHandle handle = Alloc(); + + if (PlatformDetection.IsPreciseGcSupported) + { + while (!IsNullTarget(handle)) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + } + + handle.Free(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static GCHandle Alloc() + { + GCHandle gcHandle = default; + gcHandle = GCHandle.Alloc(new FreachableObject(), GCHandleType.WeakTrackResurrection); + return gcHandle; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool IsNullTarget(GCHandle handle) + { + return handle.Target is null; + } + } + + private class FreachableObject + { + private readonly Action _del; + private readonly IntPtr _fnptr; + private int _count; + + internal FreachableObject() + { + _del = new Action(SomeFunction); + _fnptr = Marshal.GetFunctionPointerForDelegate(_del); + } + + // Note: This method cannot be replaced by a lambda for the test to trigger the delegate resurrection + private void SomeFunction() + { + } + + ~FreachableObject() + { + Assert.Same(Marshal.GetDelegateForFunctionPointer(_fnptr), _del); + + if (_count++ < 3) + { + GC.ReRegisterForFinalize(this); + } + } + } + public delegate void GenericDelegate(T t); public delegate void NonGenericDelegate(string t); public delegate void OtherNonGenericDelegate(string t); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/MulticastDelegateTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/MulticastDelegateTests.cs index 354f997ed5c7b..9994fa02f10fa 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/MulticastDelegateTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/MulticastDelegateTests.cs @@ -66,6 +66,21 @@ public static void EqualsTest() Assert.Equal(d1.GetHashCode(), d1.GetHashCode()); } + [Fact] + public static void ArrayDelegates() + { + // Delegate implementation may use Delegate[] arrays as sentinels. Validate that + // the sentinels are not confused with user provided targets. + + Action da = new Delegate[5].MyExtension; + Assert.True(da.HasSingleTarget); + Assert.Equal(1, da.GetInvocationList().Length); + + Func dd = new Delegate[10].GetLength; + Assert.True(dd.HasSingleTarget); + Assert.Equal(1, dd.GetInvocationList().Length); + } + [Fact] public static void CombineReturn() { @@ -199,7 +214,7 @@ private static void CheckInvokeList(D[] expected, D combo, Tracker target) { CheckIsSingletonDelegate((D)(expected[i]), (D)(invokeList[i]), target); } - Assert.Same(combo.Target, expected[expected.Length - 1].Target); + Assert.Same(combo.Target, expected[^1].Target); Assert.Same(combo.Target, target); Assert.Equal(combo.HasSingleTarget, invokeList.Length == 1); int count = 0; @@ -209,6 +224,7 @@ private static void CheckInvokeList(D[] expected, D combo, Tracker target) count++; } Assert.Equal(count, invokeList.Length); + Assert.Equal(combo.Method, invokeList[^1].Method); } private static void CheckIsSingletonDelegate(D expected, D actual, Tracker target) @@ -283,4 +299,11 @@ private class C public string Goo(int x) => new string('A', x); } } + + static class MulticastDelegateTestsExtensions + { + public static void MyExtension(this Delegate[] delegates) + { + } + } } 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 9f71ff470f3e4..98ade01313be2 100644 --- a/src/mono/System.Private.CoreLib/src/System/Delegate.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/Delegate.Mono.cs @@ -556,7 +556,7 @@ private DelegateData CreateDelegateData() return delegate_data; } - private static bool InternalEqualTypes(object source, object value) + internal static bool InternalEqualTypes(object source, object value) { return source.GetType() == value.GetType(); } 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 e634afd23448d..73aacd9ca0e0c 100644 --- a/src/mono/System.Private.CoreLib/src/System/MulticastDelegate.Mono.cs +++ b/src/mono/System.Private.CoreLib/src/System/MulticastDelegate.Mono.cs @@ -131,6 +131,10 @@ protected sealed override Delegate CombineImpl(Delegate? follow) if (follow == null) return this; + // Verify that the types are the same... + if (!InternalEqualTypes(this, follow)) + throw new ArgumentException(SR.Arg_DlgtTypeMis); + MulticastDelegate other = (MulticastDelegate)follow; MulticastDelegate ret = AllocDelegateLike_internal(this); diff --git a/src/tests/CoreMangLib/system/delegate/delegate/DelegateCombine1.csproj b/src/tests/CoreMangLib/system/delegate/delegate/DelegateCombine1.csproj index 62c497a099222..9ba31e8110097 100644 --- a/src/tests/CoreMangLib/system/delegate/delegate/DelegateCombine1.csproj +++ b/src/tests/CoreMangLib/system/delegate/delegate/DelegateCombine1.csproj @@ -1,7 +1,5 @@ - - true true 1 diff --git a/src/tests/CoreMangLib/system/delegate/delegate/DelegateCombineImpl.csproj b/src/tests/CoreMangLib/system/delegate/delegate/DelegateCombineImpl.csproj index df8e80338fad0..bcdc37ff1fd6b 100644 --- a/src/tests/CoreMangLib/system/delegate/delegate/DelegateCombineImpl.csproj +++ b/src/tests/CoreMangLib/system/delegate/delegate/DelegateCombineImpl.csproj @@ -1,7 +1,5 @@ - - true true 1 diff --git a/src/tests/CoreMangLib/system/delegate/delegate/DelegateEquals1.csproj b/src/tests/CoreMangLib/system/delegate/delegate/DelegateEquals1.csproj index 2d8fbf191cbb5..675f0977c04bf 100644 --- a/src/tests/CoreMangLib/system/delegate/delegate/DelegateEquals1.csproj +++ b/src/tests/CoreMangLib/system/delegate/delegate/DelegateEquals1.csproj @@ -1,7 +1,5 @@ - - true true 1 diff --git a/src/tests/CoreMangLib/system/delegate/delegate/DelegateGetHashCode1.csproj b/src/tests/CoreMangLib/system/delegate/delegate/DelegateGetHashCode1.csproj index a597f02daeda1..93d280cc34678 100644 --- a/src/tests/CoreMangLib/system/delegate/delegate/DelegateGetHashCode1.csproj +++ b/src/tests/CoreMangLib/system/delegate/delegate/DelegateGetHashCode1.csproj @@ -1,7 +1,5 @@ - - true true 1 diff --git a/src/tests/CoreMangLib/system/delegate/delegate/DelegateGetInvocationList1.csproj b/src/tests/CoreMangLib/system/delegate/delegate/DelegateGetInvocationList1.csproj deleted file mode 100644 index e82724aaace7f..0000000000000 --- a/src/tests/CoreMangLib/system/delegate/delegate/DelegateGetInvocationList1.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - true - true - 1 - - - - - - - - diff --git a/src/tests/CoreMangLib/system/delegate/delegate/DelegateRemove.csproj b/src/tests/CoreMangLib/system/delegate/delegate/DelegateRemove.csproj index 0217c4ad8d879..fccdb6634bb1b 100644 --- a/src/tests/CoreMangLib/system/delegate/delegate/DelegateRemove.csproj +++ b/src/tests/CoreMangLib/system/delegate/delegate/DelegateRemove.csproj @@ -1,7 +1,5 @@ - - true true 1 diff --git a/src/tests/CoreMangLib/system/delegate/delegate/delegateRemoveImpl.csproj b/src/tests/CoreMangLib/system/delegate/delegate/delegateRemoveImpl.csproj index ab802b989cda7..8ef2b3c7819d3 100644 --- a/src/tests/CoreMangLib/system/delegate/delegate/delegateRemoveImpl.csproj +++ b/src/tests/CoreMangLib/system/delegate/delegate/delegateRemoveImpl.csproj @@ -1,7 +1,5 @@ - - true true 1 diff --git a/src/tests/CoreMangLib/system/delegate/delegate/delegategetinvocationlist1.cs b/src/tests/CoreMangLib/system/delegate/delegate/delegategetinvocationlist1.cs deleted file mode 100644 index d0c8f34327362..0000000000000 --- a/src/tests/CoreMangLib/system/delegate/delegate/delegategetinvocationlist1.cs +++ /dev/null @@ -1,230 +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; -using System.Globalization; -using Xunit; -//test case for delegate GetInvocationList method. -namespace DelegateTest -{ - delegate bool booldelegate(); - public class DelegateGetInvocationList - { - - booldelegate starkWork; - - [Fact] - public static int TestEntryPoint() - { - DelegateGetInvocationList delegateGetInvocationList = new DelegateGetInvocationList(); - - TestLibrary.TestFramework.BeginTestCase("DelegateGetInvocationList"); - - if (delegateGetInvocationList.RunTests()) - { - TestLibrary.TestFramework.EndTestCase(); - TestLibrary.TestFramework.LogInformation("PASS"); - return 100; - - } - else - { - TestLibrary.TestFramework.EndTestCase(); - TestLibrary.TestFramework.LogInformation("FAIL"); - return 0; - } - } - - public bool RunTests() - { - bool retVal = true; - - TestLibrary.TestFramework.LogInformation("[Positive]"); - retVal = PosTest1() && retVal; - retVal = PosTest2() && retVal; - retVal = PosTest3() && retVal; - retVal = PosTest4() && retVal; - return retVal; - } - - // Returns true if the expected result is right - // Returns false if the expected result is wrong - public bool PosTest1() - { - bool retVal = true; - - TestLibrary.TestFramework.BeginScenario("PosTest1: Call GetInvocationList against a delegate with one function"); - try - { - DelegateGetInvocationList delctor = new DelegateGetInvocationList(); - booldelegate dStartWork_Bool = new booldelegate(new TestClass().StartWork_Bool); - delctor.starkWork = dStartWork_Bool; - Delegate[] invocationList = delctor.starkWork.GetInvocationList(); - if (invocationList.Length != 1) - { - TestLibrary.TestFramework.LogError("001", "Call GetInvocationList against a delegate with one function returns wrong result: " + invocationList.Length); - retVal = false; - } - if (!delctor.starkWork.GetInvocationList()[0].Equals(dStartWork_Bool)) - { - TestLibrary.TestFramework.LogError("002", " GetInvocationList return error method "); - retVal = false; - } - delctor.starkWork(); - } - catch (Exception e) - { - TestLibrary.TestFramework.LogError("003", "Unexpected exception: " + e); - retVal = false; - } - - return retVal; - } - // Returns true if the expected result is right - // Returns false if the expected result is wrong - public bool PosTest2() - { - bool retVal = true; - - TestLibrary.TestFramework.BeginScenario("PosTest2: Call GetInvocationList against a delegate with muti different functions "); - try - { - DelegateGetInvocationList delctor = new DelegateGetInvocationList(); - booldelegate bStartWork_Bool = new booldelegate(new TestClass().StartWork_Bool); - booldelegate bWorking_Bool = new booldelegate(new TestClass().Working_Bool); - booldelegate bCompleted_Bool = new booldelegate(new TestClass().Completed_Bool); - - delctor.starkWork += bStartWork_Bool; - delctor.starkWork += bWorking_Bool; - delctor.starkWork += bCompleted_Bool; - Delegate[] invocationList = delctor.starkWork.GetInvocationList(); - if (invocationList.Length != 3) - { - TestLibrary.TestFramework.LogError("004", "Call GetInvocationList against a delegate with one function returns wrong result: " + invocationList.Length); - retVal = false; - } - if (!delctor.starkWork.GetInvocationList()[0].Equals(bStartWork_Bool) - || !delctor.starkWork.GetInvocationList()[1].Equals(bWorking_Bool) - || !delctor.starkWork.GetInvocationList()[2].Equals(bCompleted_Bool)) - { - TestLibrary.TestFramework.LogError("005", " GetInvocationList return error method "); - retVal = false; - } - delctor.starkWork(); - } - catch (Exception e) - { - TestLibrary.TestFramework.LogError("006", "Unexpected exception: " + e); - retVal = false; - } - - return retVal; - } - // Returns true if the expected result is right - // Returns false if the expected result is wrong - public bool PosTest3() - { - bool retVal = true; - - TestLibrary.TestFramework.BeginScenario("PosTest3: Call GetInvocationList against a delegate with muti functions ,some is null"); - try - { - DelegateGetInvocationList delctor = new DelegateGetInvocationList(); - booldelegate bStartWork_Bool = new booldelegate(new TestClass().StartWork_Bool); - booldelegate bWorking_Bool = new booldelegate(new TestClass().Working_Bool); - booldelegate bCompleted_Bool = new booldelegate(new TestClass().Completed_Bool); - - delctor.starkWork += bStartWork_Bool; - delctor.starkWork += null; - delctor.starkWork += bWorking_Bool; - delctor.starkWork += bCompleted_Bool; - Delegate[] invocationList = delctor.starkWork.GetInvocationList(); - if (invocationList.Length != 3) - { - TestLibrary.TestFramework.LogError("007", "Call GetInvocationList against a delegate with one function returns wrong result: " + invocationList.Length); - retVal = false; - } - if (!delctor.starkWork.GetInvocationList()[0].Equals(bStartWork_Bool) - || !delctor.starkWork.GetInvocationList()[1].Equals(bWorking_Bool) - || !delctor.starkWork.GetInvocationList()[2].Equals(bCompleted_Bool)) - { - TestLibrary.TestFramework.LogError("008", " GetInvocationList return error method "); - retVal = false; - } - delctor.starkWork(); - } - catch (Exception e) - { - TestLibrary.TestFramework.LogError("009", "Unexpected exception: " + e); - retVal = false; - } - - return retVal; - } - - // Returns true if the expected result is right - // Returns false if the expected result is wrong - public bool PosTest4() - { - bool retVal = true; - - TestLibrary.TestFramework.BeginScenario("PosTest4: Call GetInvocationList against a delegate with muti functions ,some of these are the same"); - try - { - DelegateGetInvocationList delctor = new DelegateGetInvocationList(); - booldelegate bStartWork_Bool = new booldelegate(new TestClass().StartWork_Bool); - booldelegate bWorking_Bool = new booldelegate(new TestClass().Working_Bool); - booldelegate bCompleted_Bool = new booldelegate(new TestClass().Completed_Bool); - - delctor.starkWork += bStartWork_Bool; - delctor.starkWork += bStartWork_Bool; - delctor.starkWork += bWorking_Bool; - delctor.starkWork += bCompleted_Bool; - Delegate[] invocationList = delctor.starkWork.GetInvocationList(); - if (invocationList.Length != 4) - { - TestLibrary.TestFramework.LogError("010", "Call GetInvocationList against a delegate with one function returns wrong result: " + invocationList.Length); - retVal = false; - } - if (!delctor.starkWork.GetInvocationList()[0].Equals(bStartWork_Bool) - || !delctor.starkWork.GetInvocationList()[1].Equals(bStartWork_Bool) - || !delctor.starkWork.GetInvocationList()[2].Equals(bWorking_Bool) - || !delctor.starkWork.GetInvocationList()[3].Equals(bCompleted_Bool)) - { - TestLibrary.TestFramework.LogError("011", " GetInvocationList return error method "); - retVal = false; - } - delctor.starkWork(); - } - catch (Exception e) - { - TestLibrary.TestFramework.LogError("012", "Unexpected exception: " + e); - retVal = false; - } - - return retVal; - } - - } - //create testclass for providing test method and test target. - class TestClass - { - public bool StartWork_Bool() - { - TestLibrary.TestFramework.LogInformation("StartWork_Bool method is running ."); - return true; - } - public bool Working_Bool() - { - TestLibrary.TestFramework.LogInformation("Working_Bool method is running ."); - return true; - } - public bool Completed_Bool() - { - TestLibrary.TestFramework.LogInformation("Completed_Bool method is running ."); - return true; - } - } - - -} diff --git a/src/tests/Interop/MarshalAPI/FunctionPointer/FunctionPointer.cs b/src/tests/Interop/MarshalAPI/FunctionPointer/FunctionPointer.cs index 3dbcb6aaf3980..7b0fd902a7bdf 100644 --- a/src/tests/Interop/MarshalAPI/FunctionPointer/FunctionPointer.cs +++ b/src/tests/Interop/MarshalAPI/FunctionPointer/FunctionPointer.cs @@ -28,8 +28,6 @@ static class FunctionPointerNative delegate void VoidDelegate(); [Fact] - - [ActiveIssue("https://github.com/dotnet/runtimelab/issues/164", typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsNativeAot))] public static void RunGetDelForFcnPtrTest() { Console.WriteLine($"Running {nameof(RunGetDelForFcnPtrTest)}..."); @@ -42,6 +40,10 @@ public static void RunGetDelForFcnPtrTest() VoidDelegate del = (VoidDelegate)Marshal.GetDelegateForFunctionPointer(fcnptr, typeof(VoidDelegate)); Assert.Equal(md.Target, del.Target); Assert.Equal(md.Method, del.Method); + + VoidDelegate del2 = (VoidDelegate)Marshal.GetDelegateForFunctionPointer(fcnptr, typeof(VoidDelegate)); + Assert.Equal(del, del2); + Assert.Equal(del.GetHashCode(), del2.GetHashCode()); } // Native FcnPtr -> Delegate @@ -51,6 +53,10 @@ public static void RunGetDelForFcnPtrTest() Assert.Null(del.Target); Assert.Equal("Invoke", del.Method.Name); + VoidDelegate del2 = (VoidDelegate)Marshal.GetDelegateForFunctionPointer(fcnptr, typeof(VoidDelegate));; + Assert.Equal(del, del2); + Assert.Equal(del.GetHashCode(), del2.GetHashCode()); + // Round trip of a native function pointer is never legal for a non-concrete Delegate type Assert.Throws(() => {