Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
269 changes: 266 additions & 3 deletions src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -411,5 +409,270 @@ 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<Delegate>(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;
}
}
}
Loading