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/System/Delegate.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs
index 1c565a045b826..9de1b4587081a 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;
@@ -45,6 +46,15 @@ protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Al
private nint m_extraFunctionPointerOrData;
private IntPtr m_functionPointer;
+ // m_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
private protected const int MulticastThunk = 0;
@@ -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,27 +83,12 @@ 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)
- {
- isInterpreterEntrypoint = true;
- return IntPtr.Zero;
- }
- else if (m_extraFunctionPointerOrData != 0)
+ if (m_extraFunctionPointerOrData != 0)
{
if (GetThunk(OpenInstanceThunk) == m_functionPointer)
{
@@ -113,13 +102,11 @@ internal unsafe IntPtr GetFunctionPointer(out RuntimeTypeHandle typeOfFirstParam
if (m_firstParameter != null)
typeOfFirstParameterIfInstanceDelegate = new RuntimeTypeHandle(m_firstParameter.GetMethodTable());
- // TODO! Implementation issue for generic invokes here ... we need another IntPtr for uniqueness.
-
return m_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)
@@ -129,7 +116,7 @@ private void InitializeClosedInstance(object firstParameter, IntPtr functionPoin
m_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.
@@ -180,6 +167,7 @@ private void InitializeClosedInstanceWithGVMResolution(object firstParameter, Ru
return;
}
+ // This function is known to the compiler.
private void InitializeClosedInstanceToInterface(object firstParameter, IntPtr dispatchCell)
{
if (firstParameter is null)
@@ -207,7 +195,7 @@ private void InitializeClosedInstanceWithoutNullCheck(object firstParameter, Int
}
}
- // 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;
@@ -216,7 +204,7 @@ private void InitializeClosedStaticThunk(object firstParameter, IntPtr functionP
m_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.
@@ -241,15 +229,7 @@ private IntPtr GetActualTargetFunctionPointer(object thisObject)
return OpenMethodResolver.ResolveMethod(m_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)
@@ -274,19 +254,31 @@ internal bool IsDynamicDelegate()
protected virtual MethodInfo GetMethodImpl()
{
+ // Multi-cast delegates return the Method of the last delegate in the list
+ if (m_helperObject is Wrapper[] invocationList)
+ {
+ int invocationCount = (int)m_extraFunctionPointerOrData;
+ return invocationList[invocationCount - 1].Value.GetMethodImpl();
+ }
+
+ // Return the delegate Invoke method for marshalled function pointers and LINQ expressions
+ if ((m_firstParameter is NativeFunctionPointerWrapper) || (m_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 (m_helperObject is Wrapper[] invocationList)
{
- Delegate[] invocationList = (Delegate[])m_helperObject;
int invocationCount = (int)m_extraFunctionPointerOrData;
- return invocationList[invocationCount - 1].Target;
+ return invocationList[invocationCount - 1].Value.Target;
}
// Closed static delegates place a value in m_helperObject that they pass to the target method.
@@ -301,6 +293,12 @@ public object Target
return null;
}
+ // NativeFunctionPointerWrapper used by marshalled function pointers is not returned as a public target
+ if (m_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;
}
@@ -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) == m_functionPointer) ? m_extraFunctionPointerOrData : 0;
+
+ internal NativeFunctionPointerWrapper? TryGetNativeFunctionPointerWrapper() => m_firstParameter as NativeFunctionPointerWrapper;
internal static unsafe bool InternalEqualTypes(object a, object b)
{
@@ -397,21 +391,14 @@ internal static unsafe Delegate CreateDelegate(MethodTable* delegateEEType, IntP
return del;
}
- private unsafe MulticastDelegate NewMulticastDelegate(Delegate[] invocationList, int invocationCount, bool thisIsMultiCastAlready = false)
+ private unsafe Delegate NewMulticastDelegate(Wrapper[] invocationList, int invocationCount, bool thisIsMultiCastAlready = false)
{
- // First, allocate a new multicast delegate just like this one, i.e. same type as the this object
- MulticastDelegate result = (MulticastDelegate)RuntimeImports.RhNewObject(this.GetMethodTable());
+ // First, allocate a new delegate just like this one, i.e. same type as the this object
+ Delegate result = Unsafe.As(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);
- }
+ // copy m_functionPointer field rather than calling GetThunk to get it
+ result.m_functionPointer = thisIsMultiCastAlready ? m_functionPointer : GetThunk(MulticastThunk);
result.m_firstParameter = result;
result.m_helperObject = invocationList;
result.m_extraFunctionPointerOrData = (IntPtr)invocationCount;
@@ -419,22 +406,19 @@ private unsafe MulticastDelegate NewMulticastDelegate(Delegate[] invocationList,
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.m_firstParameter, o.m_firstParameter) &&
+ object.ReferenceEquals(dd.m_helperObject, o.m_helperObject) &&
+ dd.m_extraFunctionPointerOrData == o.m_extraFunctionPointerOrData &&
+ dd.m_functionPointer == o.m_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.m_helperObject as Wrapper[];
if (followList != null)
- followCount = (int)dFollow.m_extraFunctionPointerOrData;
+ followCount = (int)d.m_extraFunctionPointerOrData;
int resultCount;
- Delegate[]? invocationList = m_helperObject as Delegate[];
- if (invocationList == null)
+ Wrapper[]? resultList;
+ if (m_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
{
@@ -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,17 +551,14 @@ 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.m_helperObject is not Wrapper[] dInvocationList)
{
- Delegate[]? invocationList = m_helperObject as Delegate[];
- if (invocationList == null)
+ if (m_helperObject is not Wrapper[] invocationList)
{
// they are both not real Multicast
- if (this.Equals(v))
+ if (this.Equals(d))
return null;
}
else
@@ -590,16 +566,16 @@ private static bool EqualInvocationLists(Delegate[] a, Delegate[] b, int start,
int invocationCount = (int)m_extraFunctionPointerOrData;
for (int i = invocationCount; --i >= 0;)
{
- if (v.Equals(invocationList[i]))
+ if (d.Equals(invocationList[i]))
{
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 (m_helperObject is Wrapper[] invocationList)
{
int invocationCount = (int)m_extraFunctionPointerOrData;
- int vInvocationCount = (int)v.m_extraFunctionPointerOrData;
- for (int i = invocationCount - vInvocationCount; i >= 0; i--)
+ int dInvocationCount = (int)d.m_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 (m_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];
+ 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,74 @@ 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[])
+ // 1- Multicast (m_helperObject is Wrapper[])
// 2- Single-cast delegate, which can be compared with a structural comparison
- IntPtr multicastThunk = GetThunk(MulticastThunk);
- if (m_functionPointer == multicastThunk)
+ if (m_helperObject is Wrapper[] invocationList)
{
- return d.m_functionPointer == multicastThunk && InvocationListEquals(d);
- }
- else
- {
- if (!object.ReferenceEquals(m_helperObject, d.m_helperObject) ||
- (!FunctionPointerOps.Compare(m_extraFunctionPointerOrData, d.m_extraFunctionPointerOrData)) ||
- (!FunctionPointerOps.Compare(m_functionPointer, d.m_functionPointer)))
- {
+ if (d.m_extraFunctionPointerOrData != m_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.m_helperObject is not Wrapper[] dInvocationList)
+ return false;
+
+ int invocationCount = (int)m_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 (!object.ReferenceEquals(m_helperObject, d.m_helperObject) ||
+ (!FunctionPointerOps.Compare(m_extraFunctionPointerOrData, d.m_extraFunctionPointerOrData)) ||
+ (!FunctionPointerOps.Compare(m_functionPointer, d.m_functionPointer)))
+ {
+ 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))
+ {
+ return object.ReferenceEquals(d.m_firstParameter, d);
}
+
+ return object.ReferenceEquals(m_firstParameter, d.m_firstParameter);
}
public override int GetHashCode()
{
- Delegate[]? invocationList = m_helperObject as Delegate[];
- if (invocationList == null)
- {
- return base.GetHashCode();
- }
- else
+ if (m_helperObject is Wrapper[] invocationList)
{
int hash = 0;
for (int i = 0; i < (int)m_extraFunctionPointerOrData; i++)
{
- hash = hash * 33 + invocationList[i].GetHashCode();
+ hash = hash * 33 + invocationList[i].Value.GetHashCode();
}
-
return hash;
}
+
+ return base.GetHashCode();
}
- public bool HasSingleTarget => !(m_helperObject is Delegate[]);
+ public bool HasSingleTarget => m_helperObject is not Wrapper[];
// Used by delegate invocation list enumerator
internal Delegate? TryGetAt(int index)
{
- if (!(m_helperObject is Delegate[] invocationList))
+ if (m_helperObject is Wrapper[] invocationList)
{
- return (index == 0) ? this : null;
- }
- else
- {
- return ((uint)index < (uint)m_extraFunctionPointerOrData) ? invocationList[index] : null;
+ return ((uint)index < (uint)m_extraFunctionPointerOrData) ? invocationList[index].Value : null;
}
+
+ return (index == 0) ? this : null;
}
}
}
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..87e3303b6f154 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,64 @@ 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 a weak GChandle for the delegate
- thunkData->Handle = GCHandle.Alloc(del, GCHandleType.Weak);
+ //
+ // 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));
- // 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;
- }
+ ThunkContextData* thunkData = (ThunkContextData*)ContextData;
+
+ // allocate a weak GChandle for the delegate
+ thunkData->Handle = GCHandle.Alloc(del, GCHandleType.Weak);
+ 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 (Thunk != IntPtr.Zero)
+ {
+ RuntimeAugments.FreeThunk(s_thunkPoolHeap, Thunk);
+ }
+
+ 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)
- {
- handle.Free();
- }
-
- // Free the allocated context data memory
- NativeMemory.Free((void*)ContextData);
+ handle.Free();
}
+
+ // Free the allocated context data memory
+ NativeMemory.Free((void*)ContextData);
}
}
}
@@ -179,19 +179,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/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/tests/CoreMangLib/system/delegate/delegate/DelegateCombine1.csproj b/src/tests/CoreMangLib/system/delegate/delegate/DelegateCombine1.csproj
index 62c497a099222..3ca1a050735ef 100644
--- a/src/tests/CoreMangLib/system/delegate/delegate/DelegateCombine1.csproj
+++ b/src/tests/CoreMangLib/system/delegate/delegate/DelegateCombine1.csproj
@@ -1,9 +1,7 @@
-
- true
true
- 1
+ 0
diff --git a/src/tests/CoreMangLib/system/delegate/delegate/DelegateCombineImpl.csproj b/src/tests/CoreMangLib/system/delegate/delegate/DelegateCombineImpl.csproj
index df8e80338fad0..7c4ca6e8d532b 100644
--- a/src/tests/CoreMangLib/system/delegate/delegate/DelegateCombineImpl.csproj
+++ b/src/tests/CoreMangLib/system/delegate/delegate/DelegateCombineImpl.csproj
@@ -1,9 +1,7 @@
-
- true
true
- 1
+ 0
diff --git a/src/tests/CoreMangLib/system/delegate/delegate/DelegateEquals1.csproj b/src/tests/CoreMangLib/system/delegate/delegate/DelegateEquals1.csproj
index 2d8fbf191cbb5..07251f373e3ed 100644
--- a/src/tests/CoreMangLib/system/delegate/delegate/DelegateEquals1.csproj
+++ b/src/tests/CoreMangLib/system/delegate/delegate/DelegateEquals1.csproj
@@ -1,9 +1,6 @@
-
- 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..6a33e215d0fc5 100644
--- a/src/tests/CoreMangLib/system/delegate/delegate/DelegateGetHashCode1.csproj
+++ b/src/tests/CoreMangLib/system/delegate/delegate/DelegateGetHashCode1.csproj
@@ -1,9 +1,6 @@
-
- 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..87fdce6e85994 100644
--- a/src/tests/CoreMangLib/system/delegate/delegate/DelegateRemove.csproj
+++ b/src/tests/CoreMangLib/system/delegate/delegate/DelegateRemove.csproj
@@ -1,9 +1,6 @@
-
- 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..6d02178792036 100644
--- a/src/tests/CoreMangLib/system/delegate/delegate/delegateRemoveImpl.csproj
+++ b/src/tests/CoreMangLib/system/delegate/delegate/delegateRemoveImpl.csproj
@@ -1,9 +1,6 @@
-
- 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(() =>
{