Repository: dotnet/runtime
File: src/coreclr/vm/comdelegate.cpp (C++)

Code before:
    MethodTable* pMTTarg = NULL;
    if (target.Get() != NULL)
        pMTTarg = target.Get()->GetMethodTable();

    MethodTable* pDelMT = refThis->GetMethodTable();
    MethodDesc* pMethOrig = NonVirtualEntry2MethodDesc(method);
    MethodDesc* pMeth = pMethOrig;
    _ASSERTE(pMeth != NULL);

    LOG((LF_STUBS, LL_INFO1000, "In DelegateConstruct: for delegate type %s binding to method %s::%s%s, static = %d\n",
         pDelMT->GetDebugClassName(),
         pMeth->m_pszDebugClassName, pMeth->m_pszDebugMethodName, pMeth->m_pszDebugMethodSignature, pMeth->IsStatic()));

#ifdef _DEBUG
    // Assert that everything is OK...This is not some bogus
    //  address...Very unlikely that the code below would work
    //  for a random address in memory....
    MethodTable* p = pMeth->GetMethodTable();
    _ASSERTE(p != NULL);
    _ASSERTE(p->ValidateWithPossibleAV());
#endif // _DEBUG

    if (Nullable::IsNullableType(pMeth->GetMethodTable()))
        COMPlusThrow(kNotSupportedException);

    // Do not allow static methods with [UnmanagedCallersOnlyAttribute] to be a delegate target.
    // A method marked UnmanagedCallersOnly is special and allowing it to be delegate target will destabilize the runtime.
    if (pMeth->HasUnmanagedCallersOnlyAttribute())
        COMPlusThrow(kNotSupportedException);

    MethodDesc* pDelegateInvoke = COMDelegate::FindDelegateInvokeMethod(pDelMT);

    UINT invokeArgCount = MethodDescToNumFixedArgs(pDelegateInvoke);
    UINT methodArgCount = MethodDescToNumFixedArgs(pMeth);
    BOOL isStatic = pMeth->IsStatic();
    if (!isStatic)
        methodArgCount++; // count 'this'

    if (COMDelegate::NeedsWrapperDelegate(pMeth))
        refThis = COMDelegate::CreateWrapperDelegate(refThis, pMeth);

    if (pMeth->GetLoaderAllocator()->IsCollectible())
        refThis->SetMethodBase(pMeth->GetLoaderAllocator()->GetExposedObject());

    // Open delegates.
    if (invokeArgCount == methodArgCount)
    {
        // set the target
        refThis->SetTarget(refThis);

        // set the shuffle thunk

Review comment (bug):
Most delegate constructor go via optimized path, so this is incomplete.

Our strategy has been to delay this check until the UnmanagedCallersOnly method is actually called and produce a fail-fast with a good error message in that case: https://github.com/dotnet/runtime/blob/a83d76a82cd0ef5fa0a8a415fe1d80681122936e/src/coreclr/vm/jithelpers.cpp#L997

Code after:
    MethodTable* pMTTarg = NULL;
    if (target.Get() != NULL)
        pMTTarg = target.Get()->GetMethodTable();

    MethodTable* pDelMT = refThis->GetMethodTable();
    MethodDesc* pMethOrig = NonVirtualEntry2MethodDesc(method);
    MethodDesc* pMeth = pMethOrig;
    _ASSERTE(pMeth != NULL);

    LOG((LF_STUBS, LL_INFO1000, "In DelegateConstruct: for delegate type %s binding to method %s::%s%s, static = %d\n",
         pDelMT->GetDebugClassName(),
         pMeth->m_pszDebugClassName, pMeth->m_pszDebugMethodName, pMeth->m_pszDebugMethodSignature, pMeth->IsStatic()));

#ifdef _DEBUG
    // Assert that everything is OK...This is not some bogus
    //  address...Very unlikely that the code below would work
    //  for a random address in memory....
    MethodTable* p = pMeth->GetMethodTable();
    _ASSERTE(p != NULL);
    _ASSERTE(p->ValidateWithPossibleAV());
#endif // _DEBUG

    if (Nullable::IsNullableType(pMeth->GetMethodTable()))
        COMPlusThrow(kNotSupportedException);

    MethodDesc* pDelegateInvoke = COMDelegate::FindDelegateInvokeMethod(pDelMT);

    UINT invokeArgCount = MethodDescToNumFixedArgs(pDelegateInvoke);
    UINT methodArgCount = MethodDescToNumFixedArgs(pMeth);
    BOOL isStatic = pMeth->IsStatic();
    if (!isStatic)
        methodArgCount++; // count 'this'

    if (COMDelegate::NeedsWrapperDelegate(pMeth))
        refThis = COMDelegate::CreateWrapperDelegate(refThis, pMeth);

    if (pMeth->GetLoaderAllocator()->IsCollectible())
        refThis->SetMethodBase(pMeth->GetLoaderAllocator()->GetExposedObject());

    // Open delegates.
    if (invokeArgCount == methodArgCount)
    {
        // set the target
        refThis->SetTarget(refThis);

        // set the shuffle thunk
        PCODE pEntryPoint = SetupShuffleThunk(pDelMT, pMeth);
        refThis->SetMethodPtr(pEntryPoint);

        // set the ptr aux according to what is needed, if virtual need to call make virtual stub dispatch
        if (!pMeth->IsStatic() && pMeth->IsVirtual() && !pMeth->GetMethodTable()->IsValueType())