Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions docs/design/datacontracts/Object.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ DelegateInfo GetDelegateInfo(TargetPointer address)
Data.Delegate del = new Data.Delegate(target, address);

// Classify the delegate from its invocation count and auxiliary pointer.
// This does not handle open virtual delegates correctly.
DelegateType delegateType = target.ReadNInt(address + /* Delegate::InvocationCount offset */) switch
{
0 => del.MethodPtrAux == TargetCodePointer.Null
Expand Down
25 changes: 5 additions & 20 deletions src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
Expand Down Expand Up @@ -218,9 +219,6 @@ protected virtual MethodInfo GetMethodImpl()
return (MethodInfo)_helperObject;
}

public object? Target => GetTarget();

// V1 API.
[RequiresUnreferencedCode("The target method might be removed")]
public static Delegate? CreateDelegate(Type type, object target, string method, bool ignoreCase, bool throwOnBindFailure)
{
Expand Down Expand Up @@ -256,7 +254,6 @@ protected virtual MethodInfo GetMethodImpl()
return d;
}

// V1 API.
public static Delegate? CreateDelegate(Type type, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.AllMethods)] Type target, string method, bool ignoreCase, bool throwOnBindFailure)
{
ArgumentNullException.ThrowIfNull(type);
Expand Down Expand Up @@ -292,7 +289,6 @@ protected virtual MethodInfo GetMethodImpl()
return d;
}

// V1 API.
public static Delegate? CreateDelegate(Type type, MethodInfo method, bool throwOnBindFailure)
{
ArgumentNullException.ThrowIfNull(type);
Expand Down Expand Up @@ -327,7 +323,6 @@ protected virtual MethodInfo GetMethodImpl()
return d;
}

// V2 API.
public static Delegate? CreateDelegate(Type type, object? firstArgument, MethodInfo method, bool throwOnBindFailure)
{
ArgumentNullException.ThrowIfNull(type);
Expand Down Expand Up @@ -359,12 +354,8 @@ protected virtual MethodInfo GetMethodImpl()
return d;
}

//
// internal implementation details (FCALLS and utilities)
//

// V2 internal API.
internal static Delegate CreateDelegateNoSecurityCheck(Type type, object? target, RuntimeMethodHandle method)
internal static Delegate CreateDelegateForDynamicMethod(Type type, object? target, RuntimeMethodHandle method,
DynamicMethod dynamicMethod)
{
ArgumentNullException.ThrowIfNull(type);

Expand All @@ -377,10 +368,7 @@ internal static Delegate CreateDelegateNoSecurityCheck(Type type, object? target
if (!rtType.IsDelegate())
throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(type));

// Initialize the method...
Delegate d = InternalAlloc(rtType);
// This is a new internal API added in Whidbey. Currently it's only
// used by the dynamic method code to generate a wrapper delegate.
// Allow flexible binding options since the target method is
// unambiguously provided to us.

Expand All @@ -389,6 +377,8 @@ internal static Delegate CreateDelegateNoSecurityCheck(Type type, object? target
RuntimeMethodHandle.GetDeclaringType(method.GetMethodInfo()),
DelegateBindingFlags.RelaxedSignature))
throw new ArgumentException(SR.Arg_DlgtTargMeth);

d._helperObject = dynamicMethod;
return d;
}

Expand Down Expand Up @@ -549,11 +539,6 @@ internal void InitializeVirtualCallStub(IntPtr methodPtr)

[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "Delegate_InitializeVirtualCallStub")]
private static partial void InitializeVirtualCallStub(ObjectHandleOnStack d, IntPtr methodPtr);

internal virtual object? GetTarget()
{
return (_methodPtrAux == IntPtr.Zero) ? _target : null;
}
}

// These flags effect the way BindToMethodInfo and BindToMethodName are allowed to bind a delegate to a target method. Their
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,19 @@ namespace System
[ComVisible(true)]
public abstract partial class MulticastDelegate : Delegate
{
private object? _invocationList;

// This is set under 3 circumstances
// 1. Multicast delegate
// 2. Unmanaged function pointer
// 3. Open virtual delegate
private object? _invocationList; // Initialized by VM as needed
private nint _invocationCount;

internal bool IsUnmanagedFunctionPtr()
private bool IsUnmanagedFunctionPtr()
{
return _invocationCount == -1;
}

internal bool InvocationListLogicallyNull()
{
return (_invocationList == null) || (_invocationList is LoaderAllocator) || (_invocationList is DynamicResolver);
}

[Obsolete(Obsoletions.LegacyFormatterImplMessage, DiagnosticId = Obsoletions.LegacyFormatterImplDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override void GetObjectData(SerializationInfo info, StreamingContext context)
Expand Down Expand Up @@ -63,9 +59,9 @@ public sealed override bool Equals([NotNullWhen(true)] object? obj)
// there are 3 kind of delegate kinds that fall into this bucket
// 1- Multicast (_invocationList is Object[])
// 2- Unmanaged FntPtr (_invocationList == null)
// 3- Open virtual (_invocationCount == MethodDesc of target, _invocationList == null, LoaderAllocator, or DynamicResolver)
// 3- Open virtual (_invocationCount == MethodDesc of target, _invocationList == null)

if (InvocationListLogicallyNull())
if (HasSingleTarget)
{
if (IsUnmanagedFunctionPtr())
{
Expand Down Expand Up @@ -164,12 +160,6 @@ internal MulticastDelegate NewMulticastDelegate(object[] invocationList, int inv
return NewMulticastDelegate(invocationList, invocationCount, false);
}

internal void StoreDynamicMethod(MethodInfo dynamicMethod)
{
Debug.Assert(_invocationCount == 0);
Comment thread
MichalPetryka marked this conversation as resolved.
_helperObject = dynamicMethod;
}

// This method will combine this delegate with the passed delegate
// to form a new delegate.
internal new Delegate CombineImpl(Delegate? follow)
Expand Down Expand Up @@ -382,7 +372,7 @@ private static bool EqualInvocationLists(object[] a, object[] b, int start, int
return del;
}

internal new bool HasSingleTarget => _invocationList is not object[];
internal new bool HasSingleTarget => _invocationList is null;

// Used by delegate invocation list enumerator
internal object? /* Delegate? */ TryGetAt(int index)
Expand Down Expand Up @@ -418,29 +408,19 @@ public sealed override int GetHashCode()
}
}

internal override object? GetTarget()
internal new object? Target
{
if (_invocationCount != 0)
get
{
// _invocationCount != 0 we are in one of these cases:
// - Multicast -> return the target of the last delegate in the list
// - unmanaged function pointer - return null
// - virtual open delegate - return null
if (InvocationListLogicallyNull())
{
// both open virtual and ftn pointer return null for the target
return null;
}
else
Delegate instance = this;
if (_invocationList is object[] invocationList)
{
if (_invocationList is object[] invocationList)
{
int invocationCount = (int)_invocationCount;
return ((Delegate)invocationList[invocationCount - 1]).GetTarget();
}
// Multicast -> return the target of the last delegate in the list
int invocationCount = (int)_invocationCount;
instance = (Delegate)invocationList[invocationCount - 1];
}
return instance._methodPtrAux == 0 ? instance._target : null;
}
return base.GetTarget();
}

protected override MethodInfo GetMethodImpl()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,7 @@ public sealed override Delegate CreateDelegate(Type delegateType, object? target
GC.KeepAlive(methodHandle);
}

MulticastDelegate d = (MulticastDelegate)Delegate.CreateDelegateNoSecurityCheck(delegateType, target, GetMethodDescriptor());
// stash this MethodInfo by brute force.
d.StoreDynamicMethod(this);
return d;
return Delegate.CreateDelegateForDynamicMethod(delegateType, target, GetMethodDescriptor(), this);
}

// This is guaranteed to return a valid handle
Expand Down
3 changes: 1 addition & 2 deletions src/coreclr/debug/daccess/dacdbiimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3267,8 +3267,7 @@ DacDbiInterfaceImpl::DelegateType DacDbiInterfaceImpl::GetDelegateType(VMPTR_Obj
if (invocationCount == 0)
{
// If this delegate points to a static function or this is a open virtual delegate, this should be non-null
// Special case: This might fail in a VSD delegate (instance open virtual)...
// TODO: There is the special signatures cases missing.
// This does not handle open virtual delegates correctly.
TADDR targetMethodPtr = PCODEToPINSTR(pDelObj->GetMethodPtrAux());
if (targetMethodPtr == (TADDR)NULL)
{
Expand Down
60 changes: 14 additions & 46 deletions src/coreclr/vm/comdelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@
#include "comcallablewrapper.h"
#endif // FEATURE_COMINTEROP

#define DELEGATE_MARKER_UNMANAGEDFPTR -1


#ifndef DACCESS_COMPILE

#if defined(TARGET_X86)
Expand Down Expand Up @@ -1812,61 +1809,33 @@ MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate)

// If you modify this logic, please update cDAC IObject.GetDelegateInfo.

MethodDesc *pMethodHandle = NULL;

DELEGATEREF thisDel = (DELEGATEREF) orDelegate;
DELEGATEREF innerDel = NULL;

INT_PTR count = thisDel->GetInvocationCount();
if (count != 0)
{
// this is one of the following:
// - multicast - _invocationList is Array && _invocationCount != 0
// - unamanaged ftn ptr - _invocationList == NULL && _invocationCount == -1
// - unamanaged ftn ptr - _invocationList == null && _invocationCount == -1
// - virtual delegate - _invocationList == null && _invocationCount == (target MethodDesc)
// or _invocationList points to a LoaderAllocator/DynamicResolver
innerDel = (DELEGATEREF) thisDel->GetInvocationList();
bool fOpenVirtualDelegate = false;

if (innerDel != NULL)
{
MethodTable *pMT = innerDel->GetMethodTable();
if (pMT->IsDelegate())
return GetMethodDesc(innerDel);
if (!pMT->IsArray())
{
// must be a virtual one
fOpenVirtualDelegate = true;
}
}
else
{
if (count != DELEGATE_MARKER_UNMANAGEDFPTR)
{
// must be a virtual one
fOpenVirtualDelegate = true;
}
}
// we return the method desc for the invoke for the first two cases
OBJECTREF invocationList = thisDel->GetInvocationList();
if (invocationList != NULL || count == DELEGATE_MARKER_UNMANAGEDFPTR)
return FindDelegateInvokeMethod(thisDel->GetMethodTable());

if (fOpenVirtualDelegate)
pMethodHandle = GetMethodDescForOpenVirtualDelegate(thisDel);
else
pMethodHandle = FindDelegateInvokeMethod(thisDel->GetMethodTable());
return GetMethodDescForOpenVirtualDelegate(thisDel);
}
else
{
// Next, check for an open delegate
PCODE code = thisDel->GetMethodPtrAux();

if (code == (PCODE)NULL)
{
// Must be a normal delegate
code = thisDel->GetMethodPtr();
}

pMethodHandle = NonVirtualEntry2MethodDesc(code);
// Next, check for an open delegate
PCODE code = thisDel->GetMethodPtrAux();
if (code == (PCODE)NULL)
{
// Must be a closed delegate
code = thisDel->GetMethodPtr();
}

MethodDesc *pMethodHandle = NonVirtualEntry2MethodDesc(code);
_ASSERTE(pMethodHandle);
return pMethodHandle;
}
Expand All @@ -1889,8 +1858,7 @@ BOOL COMDelegate::IsTrueMulticastDelegate(OBJECTREF delegate)
OBJECTREF invocationList = ((DELEGATEREF)delegate)->GetInvocationList();
if (invocationList != NULL)
{
MethodTable *pMT = invocationList->GetMethodTable();
isMulticast = pMT->IsArray();
isMulticast = TRUE;
}
}

Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -1743,6 +1743,7 @@ typedef BStrWrapper* BSTRWRAPPEROBJECTREF;

#endif // FEATURE_COMINTEROP

#define DELEGATE_MARKER_UNMANAGEDFPTR (-1)

// This class corresponds to System.MulticastDelegate on the managed side.
class DelegateObject : public Object
Expand Down
33 changes: 0 additions & 33 deletions src/coreclr/vm/stubmgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1242,39 +1242,6 @@ BOOL StubLinkStubManager::TraceDelegateObject(BYTE* pbDel, TraceDestination *tra
return res;
}

// invocationList is not null, so it can be one of the following:
// Multicast, Static closed (special sig), Secure

// rule out the static with special sig
BYTE *pbCount = *(BYTE **)(pbDel + DelegateObject::GetOffsetOfInvocationCount());
if (pbCount == NULL)
{
// it's a static closed, the target lives in _methodAuxPtr
ppbDest = (BYTE **)(pbDel + DelegateObject::GetOffsetOfMethodPtrAux());

if (*ppbDest == NULL)
{
// it's not looking good, bail out
LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: can't trace into it\n"));
return FALSE;
}

LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: ppbDest: %p *ppbDest:%p\n", ppbDest, *ppbDest));

BOOL res = StubManager::TraceStub((PCODE) (*ppbDest), trace);

LOG((LF_CORDB,LL_INFO10000, "SLSM::TDO: res: %d, result type: %d\n", (res ? "true" : "false"), trace->GetTraceType()));

return res;
}

MethodTable *pType = *(MethodTable**)pbDelInvocationList;
if (pType->IsDelegate())
{
// this is a secure delegate. The target is hidden inside this field, so recurse.
return TraceDelegateObject(pbDelInvocationList, trace);
}

// Otherwise, we're going for the first invoke of the multi case.
// In order to go to the correct spot, we have just have to fish out
// slot 0 of the invocation list, and figure out where that's going to,
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/Delegate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ public abstract partial class Delegate : ICloneable, ISerializable
/// </summary>
/// <value>true if the <see cref="Delegate"/> has a single invocation target.</value>
public bool HasSingleTarget => Unsafe.As<MulticastDelegate>(this).HasSingleTarget;

public object? Target => Unsafe.As<MulticastDelegate>(this).Target;
#endif

/// <summary>
Expand Down
2 changes: 0 additions & 2 deletions src/mono/System.Private.CoreLib/src/System/Delegate.Mono.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ protected Delegate([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Al
};
}

public object? Target => GetTarget();

internal virtual object? GetTarget() => _target;

public static Delegate CreateDelegate(Type type, object? firstArgument, MethodInfo method, bool throwOnBindFailure)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ private static int LastIndexOf(Delegate[] haystack, Delegate[] needle)
}
}

internal new object? Target => GetTarget();

internal override object? GetTarget()
{
return delegates?.Length > 0 ? delegates[delegates.Length - 1].GetTarget() : base.GetTarget();
Expand Down
Loading