diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
index 08f6699e0a02db..4afe69cdc34e17 100644
--- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -151,7 +151,7 @@
-
+
diff --git a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.cs b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs
similarity index 93%
rename from src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.cs
rename to src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs
index 5863897b75e48a..ab63d4cf83b74d 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/MulticastDelegate.CoreCLR.cs
@@ -14,7 +14,7 @@ namespace System
{
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
- public abstract class MulticastDelegate : Delegate
+ public abstract partial class MulticastDelegate : Delegate
{
// This is set under 2 circumstances
// 1. Multicast delegate
@@ -22,21 +22,6 @@ public abstract class MulticastDelegate : Delegate
private object? _invocationList; // Initialized by VM as needed
private nint _invocationCount;
- // This constructor is called from the class generated by the
- // compiler generated code (This must match the constructor
- // in Delegate
- [RequiresUnreferencedCode("The target method might be removed")]
- protected MulticastDelegate(object target, string method) : base(target, method)
- {
- }
-
- // This constructor is called from a class to generate a
- // delegate based upon a static method name and the Type object
- // for the class defining the method.
- protected MulticastDelegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type target, string method) : base(target, method)
- {
- }
-
internal bool IsUnmanagedFunctionPtr()
{
return _invocationCount == -1;
@@ -448,34 +433,6 @@ public sealed override Delegate[] GetInvocationList()
}
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool operator ==(MulticastDelegate? d1, MulticastDelegate? d2)
- {
- // Test d2 first to allow branch elimination when inlined for null checks (== null)
- // so it can become a simple test
- if (d2 is null)
- {
- return d1 is null;
- }
-
- return ReferenceEquals(d2, d1) ? true : d2.Equals((object?)d1);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool operator !=(MulticastDelegate? d1, MulticastDelegate? d2)
- {
- // Can't call the == operator as it will call object==
-
- // Test d2 first to allow branch elimination when inlined for not null checks (!= null)
- // so it can become a simple test
- if (d2 is null)
- {
- return d1 is not null;
- }
-
- return ReferenceEquals(d2, d1) ? false : !d2.Equals(d1);
- }
-
public sealed override int GetHashCode()
{
if (IsUnmanagedFunctionPtr())
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
index e9e99968b9433b..4f7702c821c20d 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj
@@ -183,7 +183,6 @@
-
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 6480d45d0f5f92..1c565a045b8265 100644
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs
+++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs
@@ -1,18 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime;
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using System.Runtime.Serialization;
-using System.Text;
using Internal.Reflection.Augments;
using Internal.Runtime;
-using Internal.Runtime.Augments;
using Internal.Runtime.CompilerServices;
namespace System
@@ -42,10 +40,10 @@ protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Al
// New Delegate Implementation
- internal object m_firstParameter;
- internal object m_helperObject;
- internal nint m_extraFunctionPointerOrData;
- internal IntPtr m_functionPointer;
+ private object m_firstParameter;
+ private object m_helperObject;
+ private nint m_extraFunctionPointerOrData;
+ private IntPtr m_functionPointer;
// 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
@@ -243,11 +241,6 @@ private IntPtr GetActualTargetFunctionPointer(object thisObject)
return OpenMethodResolver.ResolveMethod(m_extraFunctionPointerOrData, thisObject);
}
- public override int GetHashCode()
- {
- return GetType().GetHashCode();
- }
-
internal bool IsDynamicDelegate()
{
if (this.GetThunk(MulticastThunk) == IntPtr.Zero)
@@ -284,14 +277,6 @@ protected virtual MethodInfo GetMethodImpl()
return ReflectionAugments.ReflectionCoreCallbacks.GetDelegateMethod(this);
}
- public override bool Equals([NotNullWhen(true)] object? obj)
- {
- // It is expected that all real uses of the Equals method will hit the MulticastDelegate.Equals logic instead of this
- // therefore, instead of duplicating the desktop behavior where direct calls to this Equals function do not behave
- // correctly, we'll just throw here.
- throw new PlatformNotSupportedException();
- }
-
public object Target
{
get
@@ -411,5 +396,365 @@ internal static unsafe Delegate CreateDelegate(MethodTable* delegateEEType, IntP
}
return del;
}
+
+ private unsafe MulticastDelegate NewMulticastDelegate(Delegate[] 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());
+
+ // 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;
+
+ return result;
+ }
+
+ private static bool TrySetSlot(Delegate[] a, int index, Delegate o)
+ {
+ if (a[index] == null && System.Threading.Interlocked.CompareExchange(ref a[index], o, null) == null)
+ return true;
+
+ // The slot may be already set because we have added and removed the same method before.
+ // Optimize this case, because it's cheaper than copying the array.
+ if (a[index] != null)
+ {
+ 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)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // This method will combine this delegate with the passed delegate
+ // to form a new delegate.
+ protected virtual Delegate CombineImpl(Delegate? d)
+ {
+ if (d is null) // cast to object for a more efficient test
+ return this;
+
+ // Verify that the types are the same...
+ if (!InternalEqualTypes(this, d))
+ throw new ArgumentException(SR.Arg_DlgtTypeMis);
+
+ if (IsDynamicDelegate() && d.IsDynamicDelegate())
+ {
+ throw new InvalidOperationException();
+ }
+
+ MulticastDelegate dFollow = (MulticastDelegate)d;
+ Delegate[]? resultList;
+ int followCount = 1;
+ Delegate[]? followList = dFollow.m_helperObject as Delegate[];
+ if (followList != null)
+ followCount = (int)dFollow.m_extraFunctionPointerOrData;
+
+ int resultCount;
+ Delegate[]? invocationList = m_helperObject as Delegate[];
+ if (invocationList == null)
+ {
+ resultCount = 1 + followCount;
+ resultList = new Delegate[resultCount];
+ resultList[0] = this;
+ if (followList == null)
+ {
+ resultList[1] = dFollow;
+ }
+ else
+ {
+ for (int i = 0; i < followCount; i++)
+ resultList[1 + i] = followList[i];
+ }
+ return NewMulticastDelegate(resultList, resultCount);
+ }
+ else
+ {
+ int invocationCount = (int)m_extraFunctionPointerOrData;
+ resultCount = invocationCount + followCount;
+ resultList = null;
+ if (resultCount <= invocationList.Length)
+ {
+ resultList = invocationList;
+ if (followList == null)
+ {
+ if (!TrySetSlot(resultList, invocationCount, dFollow))
+ resultList = null;
+ }
+ else
+ {
+ for (int i = 0; i < followCount; i++)
+ {
+ if (!TrySetSlot(resultList, invocationCount + i, followList[i]))
+ {
+ resultList = null;
+ break;
+ }
+ }
+ }
+ }
+
+ if (resultList == null)
+ {
+ int allocCount = invocationList.Length;
+ while (allocCount < resultCount)
+ allocCount *= 2;
+
+ resultList = new Delegate[allocCount];
+
+ for (int i = 0; i < invocationCount; i++)
+ resultList[i] = invocationList[i];
+
+ if (followList == null)
+ {
+ resultList[invocationCount] = dFollow;
+ }
+ else
+ {
+ for (int i = 0; i < followCount; i++)
+ resultList[invocationCount + i] = followList[i];
+ }
+ }
+ return NewMulticastDelegate(resultList, resultCount, true);
+ }
+ }
+
+ private Delegate[] DeleteFromInvocationList(Delegate[] invocationList, int invocationCount, int deleteIndex, int deleteCount)
+ {
+ Delegate[] thisInvocationList = (Delegate[])m_helperObject;
+ int allocCount = thisInvocationList.Length;
+ while (allocCount / 2 >= invocationCount - deleteCount)
+ allocCount /= 2;
+
+ Delegate[] newInvocationList = new Delegate[allocCount];
+
+ for (int i = 0; i < deleteIndex; i++)
+ newInvocationList[i] = invocationList[i];
+
+ for (int i = deleteIndex + deleteCount; i < invocationCount; i++)
+ newInvocationList[i - deleteCount] = invocationList[i];
+
+ return newInvocationList;
+ }
+
+ private static bool EqualInvocationLists(Delegate[] a, Delegate[] b, int start, int count)
+ {
+ for (int i = 0; i < count; i++)
+ {
+ if (!(a[start + i].Equals(b[i])))
+ return false;
+ }
+ return true;
+ }
+
+ // This method currently looks backward on the invocation list
+ // for an element that has Delegate based equality with value. (Doesn't
+ // look at the invocation list.) If this is found we remove it from
+ // this list and return a new delegate. If its not found a copy of the
+ // current list is returned.
+ protected virtual Delegate? RemoveImpl(Delegate d)
+ {
+ // 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)
+ return this;
+ if (v.m_helperObject as Delegate[] == null)
+ {
+ Delegate[]? invocationList = m_helperObject as Delegate[];
+ if (invocationList == null)
+ {
+ // they are both not real Multicast
+ if (this.Equals(v))
+ return null;
+ }
+ else
+ {
+ int invocationCount = (int)m_extraFunctionPointerOrData;
+ for (int i = invocationCount; --i >= 0;)
+ {
+ if (v.Equals(invocationList[i]))
+ {
+ if (invocationCount == 2)
+ {
+ // Special case - only one value left, either at the beginning or the end
+ return invocationList[1 - i];
+ }
+ else
+ {
+ Delegate[] list = DeleteFromInvocationList(invocationList, invocationCount, i, 1);
+ return NewMulticastDelegate(list, invocationCount - 1, true);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ Delegate[]? invocationList = m_helperObject as Delegate[];
+ if (invocationList != null)
+ {
+ int invocationCount = (int)m_extraFunctionPointerOrData;
+ int vInvocationCount = (int)v.m_extraFunctionPointerOrData;
+ for (int i = invocationCount - vInvocationCount; i >= 0; i--)
+ {
+ if (EqualInvocationLists(invocationList, v.m_helperObject as Delegate[], i, vInvocationCount))
+ {
+ if (invocationCount - vInvocationCount == 0)
+ {
+ // Special case - no values left
+ return null;
+ }
+ else if (invocationCount - vInvocationCount == 1)
+ {
+ // Special case - only one value left, either at the beginning or the end
+ return invocationList[i != 0 ? 0 : invocationCount - 1];
+ }
+ else
+ {
+ Delegate[] list = DeleteFromInvocationList(invocationList, invocationCount, i, vInvocationCount);
+ return NewMulticastDelegate(list, invocationCount - vInvocationCount, true);
+ }
+ }
+ }
+ }
+ }
+
+ return this;
+ }
+
+ public virtual Delegate[] GetInvocationList()
+ {
+ Delegate[] del;
+ Delegate[]? invocationList = m_helperObject as Delegate[];
+ if (invocationList == null)
+ {
+ del = new Delegate[1];
+ del[0] = this;
+ }
+ else
+ {
+ // Create an array of delegate copies and each
+ // element into the array
+ int invocationCount = (int)m_extraFunctionPointerOrData;
+ del = new Delegate[invocationCount];
+
+ for (int i = 0; i < del.Length; i++)
+ del[i] = invocationList[i];
+ }
+ 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;
+ }
+
+ public override bool Equals([NotNullWhen(true)] object? obj)
+ {
+ if (obj == null)
+ return false;
+ if (object.ReferenceEquals(this, obj))
+ return true;
+ if (!InternalEqualTypes(this, obj))
+ return false;
+
+ // Since this is a MulticastDelegate and we know
+ // the types are the same, obj should also be a
+ // MulticastDelegate
+ Debug.Assert(obj is MulticastDelegate, "Shouldn't have failed here since we already checked the types are the same!");
+ 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 (!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
+ {
+ int hash = 0;
+ for (int i = 0; i < (int)m_extraFunctionPointerOrData; i++)
+ {
+ hash = hash * 33 + invocationList[i].GetHashCode();
+ }
+
+ return hash;
+ }
+ }
+
+ public bool HasSingleTarget => !(m_helperObject is Delegate[]);
+
+ // Used by delegate invocation list enumerator
+ internal Delegate? TryGetAt(int index)
+ {
+ if (!(m_helperObject is Delegate[] invocationList))
+ {
+ return (index == 0) ? this : null;
+ }
+ else
+ {
+ return ((uint)index < (uint)m_extraFunctionPointerOrData) ? invocationList[index] : null;
+ }
+ }
}
}
diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/MulticastDelegate.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/MulticastDelegate.cs
deleted file mode 100644
index ce32047e542727..00000000000000
--- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/MulticastDelegate.cs
+++ /dev/null
@@ -1,434 +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.ComponentModel;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Reflection;
-using System.Runtime;
-using System.Runtime.CompilerServices;
-using System.Runtime.Serialization;
-
-using Internal.Runtime.CompilerServices;
-
-namespace System
-{
- public abstract class MulticastDelegate : Delegate, ISerializable
- {
- // This constructor is called from the class generated by the
- // compiler generated code (This must match the constructor
- // in Delegate
- [RequiresUnreferencedCode("The target method might be removed")]
- protected MulticastDelegate(object target, string method) : base(target, method)
- {
- }
-
- // This constructor is called from a class to generate a
- // delegate based upon a static method name and the Type object
- // for the class defining the method.
- protected MulticastDelegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type target, string method) : base(target, method)
- {
- }
-
- 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;
- }
-
- public sealed override bool Equals([NotNullWhen(true)] object? obj)
- {
- if (obj == null)
- return false;
- if (object.ReferenceEquals(this, obj))
- return true;
- if (!InternalEqualTypes(this, obj))
- return false;
-
- // Since this is a MulticastDelegate and we know
- // the types are the same, obj should also be a
- // MulticastDelegate
- Debug.Assert(obj is MulticastDelegate, "Shouldn't have failed here since we already checked the types are the same!");
- 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 (!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 sealed override int GetHashCode()
- {
- Delegate[]? invocationList = m_helperObject as Delegate[];
- if (invocationList == null)
- {
- return base.GetHashCode();
- }
- else
- {
- int hash = 0;
- for (int i = 0; i < (int)m_extraFunctionPointerOrData; i++)
- {
- hash = hash * 33 + invocationList[i].GetHashCode();
- }
-
- return hash;
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool operator ==(MulticastDelegate? d1, MulticastDelegate? d2)
- {
- // Test d2 first to allow branch elimination when inlined for null checks (== null)
- // so it can become a simple test
- if (d2 is null)
- {
- return d1 is null;
- }
-
- return ReferenceEquals(d2, d1) ? true : d2.Equals((object?)d1);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static bool operator !=(MulticastDelegate? d1, MulticastDelegate? d2)
- {
- // Can't call the == operator as it will call object==
-
- // Test d2 first to allow branch elimination when inlined for not null checks (!= null)
- // so it can become a simple test
- if (d2 is null)
- {
- return d1 is not null;
- }
-
- return ReferenceEquals(d2, d1) ? false : !d2.Equals(d1);
- }
-
- private unsafe MulticastDelegate NewMulticastDelegate(Delegate[] 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());
-
- // 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;
-
- return result;
- }
-
- private static bool TrySetSlot(Delegate[] a, int index, Delegate o)
- {
- if (a[index] == null && System.Threading.Interlocked.CompareExchange(ref a[index], o, null) == null)
- return true;
-
- // The slot may be already set because we have added and removed the same method before.
- // Optimize this case, because it's cheaper than copying the array.
- if (a[index] != null)
- {
- 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)
- {
- return true;
- }
- }
- return false;
- }
-
-
- // This method will combine this delegate with the passed delegate
- // to form a new delegate.
- protected sealed override Delegate CombineImpl(Delegate? follow)
- {
- if (follow is null) // cast to object for a more efficient test
- return this;
-
- // Verify that the types are the same...
- if (!InternalEqualTypes(this, follow))
- throw new ArgumentException(SR.Arg_DlgtTypeMis);
-
- if (IsDynamicDelegate() && follow.IsDynamicDelegate())
- {
- throw new InvalidOperationException();
- }
-
- MulticastDelegate dFollow = (MulticastDelegate)follow;
- Delegate[]? resultList;
- int followCount = 1;
- Delegate[]? followList = dFollow.m_helperObject as Delegate[];
- if (followList != null)
- followCount = (int)dFollow.m_extraFunctionPointerOrData;
-
- int resultCount;
- Delegate[]? invocationList = m_helperObject as Delegate[];
- if (invocationList == null)
- {
- resultCount = 1 + followCount;
- resultList = new Delegate[resultCount];
- resultList[0] = this;
- if (followList == null)
- {
- resultList[1] = dFollow;
- }
- else
- {
- for (int i = 0; i < followCount; i++)
- resultList[1 + i] = followList[i];
- }
- return NewMulticastDelegate(resultList, resultCount);
- }
- else
- {
- int invocationCount = (int)m_extraFunctionPointerOrData;
- resultCount = invocationCount + followCount;
- resultList = null;
- if (resultCount <= invocationList.Length)
- {
- resultList = invocationList;
- if (followList == null)
- {
- if (!TrySetSlot(resultList, invocationCount, dFollow))
- resultList = null;
- }
- else
- {
- for (int i = 0; i < followCount; i++)
- {
- if (!TrySetSlot(resultList, invocationCount + i, followList[i]))
- {
- resultList = null;
- break;
- }
- }
- }
- }
-
- if (resultList == null)
- {
- int allocCount = invocationList.Length;
- while (allocCount < resultCount)
- allocCount *= 2;
-
- resultList = new Delegate[allocCount];
-
- for (int i = 0; i < invocationCount; i++)
- resultList[i] = invocationList[i];
-
- if (followList == null)
- {
- resultList[invocationCount] = dFollow;
- }
- else
- {
- for (int i = 0; i < followCount; i++)
- resultList[invocationCount + i] = followList[i];
- }
- }
- return NewMulticastDelegate(resultList, resultCount, true);
- }
- }
-
- private Delegate[] DeleteFromInvocationList(Delegate[] invocationList, int invocationCount, int deleteIndex, int deleteCount)
- {
- Delegate[] thisInvocationList = (Delegate[])m_helperObject;
- int allocCount = thisInvocationList.Length;
- while (allocCount / 2 >= invocationCount - deleteCount)
- allocCount /= 2;
-
- Delegate[] newInvocationList = new Delegate[allocCount];
-
- for (int i = 0; i < deleteIndex; i++)
- newInvocationList[i] = invocationList[i];
-
- for (int i = deleteIndex + deleteCount; i < invocationCount; i++)
- newInvocationList[i - deleteCount] = invocationList[i];
-
- return newInvocationList;
- }
-
- private static bool EqualInvocationLists(Delegate[] a, Delegate[] b, int start, int count)
- {
- for (int i = 0; i < count; i++)
- {
- if (!(a[start + i].Equals(b[i])))
- return false;
- }
- return true;
- }
-
- // This method currently looks backward on the invocation list
- // for an element that has Delegate based equality with value. (Doesn't
- // look at the invocation list.) If this is found we remove it from
- // this list and return a new delegate. If its not found a copy of the
- // current list is returned.
- protected sealed override Delegate? RemoveImpl(Delegate value)
- {
- // There is a special case were we are removing using a delegate as
- // the value we need to check for this case
- //
- MulticastDelegate? v = value as MulticastDelegate;
-
- if (v is null)
- return this;
- if (v.m_helperObject as Delegate[] == null)
- {
- Delegate[]? invocationList = m_helperObject as Delegate[];
- if (invocationList == null)
- {
- // they are both not real Multicast
- if (this.Equals(v))
- return null;
- }
- else
- {
- int invocationCount = (int)m_extraFunctionPointerOrData;
- for (int i = invocationCount; --i >= 0;)
- {
- if (v.Equals(invocationList[i]))
- {
- if (invocationCount == 2)
- {
- // Special case - only one value left, either at the beginning or the end
- return invocationList[1 - i];
- }
- else
- {
- Delegate[] list = DeleteFromInvocationList(invocationList, invocationCount, i, 1);
- return NewMulticastDelegate(list, invocationCount - 1, true);
- }
- }
- }
- }
- }
- else
- {
- Delegate[]? invocationList = m_helperObject as Delegate[];
- if (invocationList != null)
- {
- int invocationCount = (int)m_extraFunctionPointerOrData;
- int vInvocationCount = (int)v.m_extraFunctionPointerOrData;
- for (int i = invocationCount - vInvocationCount; i >= 0; i--)
- {
- if (EqualInvocationLists(invocationList, v.m_helperObject as Delegate[], i, vInvocationCount))
- {
- if (invocationCount - vInvocationCount == 0)
- {
- // Special case - no values left
- return null;
- }
- else if (invocationCount - vInvocationCount == 1)
- {
- // Special case - only one value left, either at the beginning or the end
- return invocationList[i != 0 ? 0 : invocationCount - 1];
- }
- else
- {
- Delegate[] list = DeleteFromInvocationList(invocationList, invocationCount, i, vInvocationCount);
- return NewMulticastDelegate(list, invocationCount - vInvocationCount, true);
- }
- }
- }
- }
- }
-
- return this;
- }
-
- public sealed override Delegate[] GetInvocationList()
- {
- Delegate[] del;
- Delegate[]? invocationList = m_helperObject as Delegate[];
- if (invocationList == null)
- {
- del = new Delegate[1];
- del[0] = this;
- }
- else
- {
- // Create an array of delegate copies and each
- // element into the array
- int invocationCount = (int)m_extraFunctionPointerOrData;
- del = new Delegate[invocationCount];
-
- for (int i = 0; i < del.Length; i++)
- del[i] = invocationList[i];
- }
- return del;
- }
-
- internal new bool HasSingleTarget => !(m_helperObject is Delegate[]);
-
- // Used by delegate invocation list enumerator
- internal Delegate? TryGetAt(int index)
- {
- if (!(m_helperObject is Delegate[] invocationList))
- {
- return (index == 0) ? this : null;
- }
- else
- {
- return ((uint)index < (uint)m_extraFunctionPointerOrData) ? invocationList[index] : null;
- }
- }
-
- protected override MethodInfo GetMethodImpl()
- {
- return base.GetMethodImpl();
- }
-
- [Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
- [EditorBrowsable(EditorBrowsableState.Never)]
- public override void GetObjectData(SerializationInfo info, StreamingContext context)
- {
- throw new PlatformNotSupportedException(SR.Serialization_DelegatesNotSupported);
- }
- }
-}
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index 9fc91793b70654..f15001de3fdf40 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -569,6 +569,7 @@
+
diff --git a/src/libraries/System.Private.CoreLib/src/System/Delegate.cs b/src/libraries/System.Private.CoreLib/src/System/Delegate.cs
index 1b861082530bf8..d24059770d5f88 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Delegate.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Delegate.cs
@@ -51,6 +51,7 @@ public abstract partial class Delegate : ICloneable, ISerializable
public static Delegate CreateDelegate(Type type, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type target, string method) => CreateDelegate(type, target, method, ignoreCase: false, throwOnBindFailure: true)!;
public static Delegate CreateDelegate(Type type, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type target, string method, bool ignoreCase) => CreateDelegate(type, target, method, ignoreCase, throwOnBindFailure: true)!;
+#if !NATIVEAOT
protected virtual Delegate CombineImpl(Delegate? d) => throw new MulticastNotSupportedException(SR.Multicast_Combine);
protected virtual Delegate? RemoveImpl(Delegate d) => d.Equals(this) ? null : this;
@@ -62,6 +63,7 @@ public abstract partial class Delegate : ICloneable, ISerializable
///
/// true if the has a single invocation target.
public bool HasSingleTarget => Unsafe.As(this).HasSingleTarget;
+#endif
///
/// Gets an enumerator for the invocation targets of this delegate.
diff --git a/src/libraries/System.Private.CoreLib/src/System/MulticastDelegate.cs b/src/libraries/System.Private.CoreLib/src/System/MulticastDelegate.cs
new file mode 100644
index 00000000000000..2508f2dc09f15a
--- /dev/null
+++ b/src/libraries/System.Private.CoreLib/src/System/MulticastDelegate.cs
@@ -0,0 +1,57 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+using System.Runtime.Serialization;
+
+namespace System
+{
+#pragma warning disable CS0660, CS0661 // Defining operators, but not overriding Equals/GetHashCode
+ public abstract partial class MulticastDelegate : Delegate, ISerializable
+ {
+ // This constructor is called from the class generated by the
+ // compiler generated code (This must match the constructor
+ // in Delegate
+ [RequiresUnreferencedCode("The target method might be removed")]
+ protected MulticastDelegate(object target, string method) : base(target, method)
+ {
+ }
+
+ // This constructor is called from a class to generate a
+ // delegate based upon a static method name and the Type object
+ // for the class defining the method.
+ protected MulticastDelegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type target, string method) : base(target, method)
+ {
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator ==(MulticastDelegate? d1, MulticastDelegate? d2)
+ {
+ // Test d2 first to allow branch elimination when inlined for null checks (== null)
+ // so it can become a simple test
+ if (d2 is null)
+ {
+ return d1 is null;
+ }
+
+ return ReferenceEquals(d2, d1) ? true : d2.Equals((object?)d1);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator !=(MulticastDelegate? d1, MulticastDelegate? d2)
+ {
+ // Can't call the == operator as it will call object==
+
+ // Test d2 first to allow branch elimination when inlined for not null checks (!= null)
+ // so it can become a simple test
+ if (d2 is null)
+ {
+ return d1 is not null;
+ }
+
+ return ReferenceEquals(d2, d1) ? false : !d2.Equals(d1);
+ }
+ }
+#pragma warning restore CS0660, CS0661
+}
diff --git a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj
index cf157fee87514c..49c5d602711719 100644
--- a/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj
+++ b/src/mono/System.Private.CoreLib/System.Private.CoreLib.csproj
@@ -172,7 +172,7 @@
-
+
diff --git a/src/mono/System.Private.CoreLib/src/System/MulticastDelegate.cs b/src/mono/System.Private.CoreLib/src/System/MulticastDelegate.Mono.cs
similarity index 91%
rename from src/mono/System.Private.CoreLib/src/System/MulticastDelegate.cs
rename to src/mono/System.Private.CoreLib/src/System/MulticastDelegate.Mono.cs
index cd297e3b221982..e634afd23448d0 100644
--- a/src/mono/System.Private.CoreLib/src/System/MulticastDelegate.cs
+++ b/src/mono/System.Private.CoreLib/src/System/MulticastDelegate.Mono.cs
@@ -10,21 +10,10 @@
namespace System
{
[StructLayout(LayoutKind.Sequential)]
- public abstract class MulticastDelegate : Delegate
+ public abstract partial class MulticastDelegate : Delegate
{
private Delegate[]? delegates;
- [RequiresUnreferencedCode("The target method might be removed")]
- protected MulticastDelegate(object target, string method)
- : base(target, method)
- {
- }
-
- protected MulticastDelegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type target, string method)
- : base(target, method)
- {
- }
-
[Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override void GetObjectData(SerializationInfo info, StreamingContext context)
@@ -275,22 +264,6 @@ private static int LastIndexOf(Delegate[] haystack, Delegate[] needle)
}
}
- public static bool operator ==(MulticastDelegate? d1, MulticastDelegate? d2)
- {
- if (d1 == null)
- return d2 == null;
-
- return d1.Equals(d2);
- }
-
- public static bool operator !=(MulticastDelegate? d1, MulticastDelegate? d2)
- {
- if (d1 == null)
- return d2 != null;
-
- return !d1.Equals(d2);
- }
-
internal override object? GetTarget()
{
return delegates?.Length > 0 ? delegates[delegates.Length - 1].GetTarget() : base.GetTarget();