Skip to content

Commit

Permalink
Change some delegate FCalls to managed (#70000)
Browse files Browse the repository at this point in the history
* Convert CompareUnmanagedFunctionPtrs to managed

* Move simple type match to managed

* Add more check in managed portion

* Rename method for FCall

* Change FCall to directly call IsEquivalentTo

* Add helper method frame

* Fix uninitialized result variable

* Change to QCall for type equivalence
huoyaoyuan authored May 31, 2022

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
1 parent 338690d commit b6c30d1
Showing 9 changed files with 47 additions and 64 deletions.
27 changes: 22 additions & 5 deletions src/coreclr/System.Private.CoreLib/src/System/Delegate.CoreCLR.cs
Original file line number Diff line number Diff line change
@@ -411,8 +411,28 @@ internal static Delegate CreateDelegateNoSecurityCheck(Type type, object? target
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern MulticastDelegate InternalAllocLike(Delegate d);

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool InternalEqualTypes(object a, object b);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static unsafe bool InternalEqualTypes(object a, object b)
{
if (a.GetType() == b.GetType())
return true;

MethodTable* pMTa = RuntimeHelpers.GetMethodTable(a);
MethodTable* pMTb = RuntimeHelpers.GetMethodTable(b);

bool ret;

// only use QCall to check the type equivalence scenario
if (pMTa->HasTypeEquivalence && pMTb->HasTypeEquivalence)
ret = RuntimeHelpers.AreTypesEquivalent(pMTa, pMTb);
else
ret = false;

GC.KeepAlive(a);
GC.KeepAlive(b);

return ret;
}

// Used by the ctor. Do not call directly.
// The name of this function will appear in managed stacktraces as delegate constructor.
@@ -441,9 +461,6 @@ internal static Delegate CreateDelegateNoSecurityCheck(Type type, object? target
{
return (_methodPtrAux == IntPtr.Zero) ? _target : null;
}

[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool CompareUnmanagedFunctionPtrs(Delegate d1, Delegate d2);
}

// These flags effect the way BindToMethodInfo and BindToMethodName are allowed to bind a delegate to a target method. Their
Original file line number Diff line number Diff line change
@@ -82,7 +82,8 @@ public sealed override bool Equals([NotNullWhen(true)] object? obj)
if (!d.IsUnmanagedFunctionPtr())
return false;

return CompareUnmanagedFunctionPtrs(this, d);
return _methodPtr == d._methodPtr
&& _methodPtrAux == d._methodPtrAux;
}

// now we know 'this' is not a special one, so we can work out what the other is
Original file line number Diff line number Diff line change
@@ -295,6 +295,11 @@ internal static unsafe bool ObjectHasComponentSize(object obj)
return (MethodTable *)Unsafe.Add(ref Unsafe.As<byte, IntPtr>(ref obj.GetRawData()), -1);
}


[LibraryImport(RuntimeHelpers.QCall, EntryPoint = "MethodTable_AreTypesEquivalent")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static unsafe partial bool AreTypesEquivalent(MethodTable* pMTa, MethodTable* pMTb);

/// <summary>
/// Allocate memory that is associated with the <paramref name="type"/> and
/// will be freed if and when the <see cref="System.Type"/> is unloaded.
52 changes: 0 additions & 52 deletions src/coreclr/vm/comdelegate.cpp
Original file line number Diff line number Diff line change
@@ -1488,34 +1488,6 @@ MethodDesc* COMDelegate::GetILStubMethodDesc(EEImplMethodDesc* pDelegateMD, DWOR
return NDirect::CreateCLRToNativeILStub(&sigInfo, dwStubFlags, pDelegateMD);
}



FCIMPL2(FC_BOOL_RET, COMDelegate::CompareUnmanagedFunctionPtrs, Object *refDelegate1UNSAFE, Object *refDelegate2UNSAFE)
{
CONTRACTL
{
FCALL_CHECK;
PRECONDITION(refDelegate1UNSAFE != NULL);
PRECONDITION(refDelegate2UNSAFE != NULL);
}
CONTRACTL_END;

DELEGATEREF refD1 = (DELEGATEREF) ObjectToOBJECTREF(refDelegate1UNSAFE);
DELEGATEREF refD2 = (DELEGATEREF) ObjectToOBJECTREF(refDelegate2UNSAFE);
BOOL ret = FALSE;

// Make sure this is an unmanaged function pointer wrapped in a delegate.
CONSISTENCY_CHECK(DELEGATE_MARKER_UNMANAGEDFPTR == refD1->GetInvocationCount());
CONSISTENCY_CHECK(DELEGATE_MARKER_UNMANAGEDFPTR == refD2->GetInvocationCount());

ret = (refD1->GetMethodPtr() == refD2->GetMethodPtr() &&
refD1->GetMethodPtrAux() == refD2->GetMethodPtrAux());

FC_RETURN_BOOL(ret);
}
FCIMPLEND


void COMDelegate::RemoveEntryFromFPtrHash(UPTR key)
{
WRAPPER_NO_CONTRACT;
@@ -2029,30 +2001,6 @@ FCIMPL1(Object*, COMDelegate::InternalAllocLike, Object* pThis)
}
FCIMPLEND

FCIMPL2(FC_BOOL_RET, COMDelegate::InternalEqualTypes, Object* pThis, Object *pThat)
{
FCALL_CONTRACT;

MethodTable *pThisMT = pThis->GetMethodTable();
MethodTable *pThatMT = pThat->GetMethodTable();

_ASSERTE(pThisMT != NULL && pThisMT->IsDelegate());
_ASSERTE(pThatMT != NULL);

BOOL bResult = (pThisMT == pThatMT);

if (!bResult)
{
HELPER_METHOD_FRAME_BEGIN_RET_0();
bResult = pThisMT->IsEquivalentTo(pThatMT);
HELPER_METHOD_FRAME_END();
}

FC_RETURN_BOOL(bResult);
}
FCIMPLEND


void COMDelegate::ThrowIfInvalidUnmanagedCallersOnlyUsage(MethodDesc* pMD)
{
CONTRACTL
4 changes: 0 additions & 4 deletions src/coreclr/vm/comdelegate.h
Original file line number Diff line number Diff line change
@@ -58,7 +58,6 @@ class COMDelegate

static FCDECL1(Object*, InternalAlloc, ReflectClassBaseObject* target);
static FCDECL1(Object*, InternalAllocLike, Object* pThis);
static FCDECL2(FC_BOOL_RET, InternalEqualTypes, Object* pThis, Object *pThat);

static FCDECL3(PCODE, AdjustTarget, Object* refThis, Object* target, PCODE method);
static FCDECL2(PCODE, GetCallStub, Object* refThis, PCODE method);
@@ -90,9 +89,6 @@ class COMDelegate
static ComPlusCallInfo * PopulateComPlusCallInfo(MethodTable * pDelMT);
#endif // FEATURE_COMINTEROP

// Checks whether two delegates wrapping function pointers have the same unmanaged target
static FCDECL2(FC_BOOL_RET, CompareUnmanagedFunctionPtrs, Object *refDelegate1UNSAFE, Object *refDelegate2UNSAFE);

static PCODE GetStubForILStub(EEImplMethodDesc* pDelegateMD, MethodDesc** ppStubMD, DWORD dwStubFlags);
static MethodDesc* GetILStubMethodDesc(EEImplMethodDesc* pDelegateMD, DWORD dwStubFlags);

15 changes: 15 additions & 0 deletions src/coreclr/vm/comutilnative.cpp
Original file line number Diff line number Diff line change
@@ -2036,6 +2036,21 @@ FCIMPL1(UINT32, MethodTableNative::GetNumInstanceFieldBytes, MethodTable* mt)
}
FCIMPLEND

extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, MethodTable* mtb)
{
QCALL_CONTRACT;

BOOL bResult = FALSE;

BEGIN_QCALL;

bResult = mta->IsEquivalentTo(mtb);

END_QCALL;

return bResult;
}

static MethodTable * g_pStreamMT;
static WORD g_slotBeginRead, g_slotEndRead;
static WORD g_slotBeginWrite, g_slotEndWrite;
2 changes: 2 additions & 0 deletions src/coreclr/vm/comutilnative.h
Original file line number Diff line number Diff line change
@@ -226,6 +226,8 @@ class MethodTableNative {
static FCDECL1(UINT32, GetNumInstanceFieldBytes, MethodTable* mt);
};

extern "C" BOOL QCALLTYPE MethodTable_AreTypesEquivalent(MethodTable* mta, MethodTable* mtb);

class StreamNative {
public:
static FCDECL1(FC_BOOL_RET, HasOverriddenBeginEndRead, Object *stream);
2 changes: 0 additions & 2 deletions src/coreclr/vm/ecalllist.h
Original file line number Diff line number Diff line change
@@ -299,12 +299,10 @@ FCFuncStart(gDelegateFuncs)
FCFuncElement("GetInvokeMethod", COMDelegate::GetInvokeMethod)
FCFuncElement("InternalAlloc", COMDelegate::InternalAlloc)
FCFuncElement("InternalAllocLike", COMDelegate::InternalAllocLike)
FCFuncElement("InternalEqualTypes", COMDelegate::InternalEqualTypes)
FCFuncElement("InternalEqualMethodHandles", COMDelegate::InternalEqualMethodHandles)
FCFuncElement("FindMethodHandle", COMDelegate::FindMethodHandle)
FCFuncElement("AdjustTarget", COMDelegate::AdjustTarget)
FCFuncElement("GetCallStub", COMDelegate::GetCallStub)
FCFuncElement("CompareUnmanagedFunctionPtrs", COMDelegate::CompareUnmanagedFunctionPtrs)

// The FCall mechanism knows how to wire multiple different constructor calls into a
// single entrypoint, without the following entry. But we need this entry to satisfy
1 change: 1 addition & 0 deletions src/coreclr/vm/qcallentrypoints.cpp
Original file line number Diff line number Diff line change
@@ -86,6 +86,7 @@ static const Entry s_QCall[] =
DllImportEntry(RuntimeTypeHandle_CreateInstanceForAnotherGenericParameter)
DllImportEntry(QCall_GetGCHandleForTypeHandle)
DllImportEntry(QCall_FreeGCHandleForTypeHandle)
DllImportEntry(MethodTable_AreTypesEquivalent)
DllImportEntry(RuntimeTypeHandle_MakePointer)
DllImportEntry(RuntimeTypeHandle_MakeByRef)
DllImportEntry(RuntimeTypeHandle_MakeSZArray)

0 comments on commit b6c30d1

Please sign in to comment.