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

Code before:
        return hash;
    }
};

// Global interpreter dispatch cache instance
static InterpDispatchCache g_InterpDispatchCache;

// Called during GC, when we are guaranteed no entry is being used by any thread.
void InterpDispatchCache_ReclaimAll()
{
    g_InterpDispatchCache.ReclaimDeadEntries();
}

static size_t CreateDispatchTokenForMethod(MethodDesc* pMD)
{
    WRAPPER_NO_CONTRACT;

    uint32_t slotNumber = pMD->GetSlot();

    if (pMD->IsInterface())
    {
        // For interface methods, get the interface's TypeID
        MethodTable* pInterfaceMT = pMD->GetMethodTable();
        uint32_t typeID = pInterfaceMT->GetTypeID();
        return DispatchToken::CreateDispatchToken(typeID, slotNumber).To_SIZE_T();
    }
    else
    {
        // For non-interface virtual methods, use TYPE_ID_THIS_CLASS
        return DispatchToken::CreateDispatchToken(slotNumber).To_SIZE_T();
    }
}

#ifdef TARGET_WASM
// Unused on WASM
#define SAVE_THE_LOWEST_SP do {} while (0)
#else
// Save the lowest SP in the current method so that we can identify it by that during stackwalk
#define SAVE_THE_LOWEST_SP pInterpreterFrame->SetInterpExecMethodSP((TADDR)GetCurrentSP())
#endif // !TARGET_WASM

// Call invoker helpers provided by platform.
void InvokeManagedMethod(MethodDesc *pMD, int8_t *pArgs, int8_t *pRet, PCODE target, Object** pContinuationRet);
void InvokeUnmanagedMethod(MethodDesc *targetMethod, int8_t *pArgs, int8_t *pRet, PCODE callTarget);
void InvokeCalliStub(PCODE ftn, void* cookie, int8_t *pArgs, int8_t *pRet, Object** pContinuationRet);
void InvokeUnmanagedCalli(PCODE ftn, void *cookie, int8_t *pArgs, int8_t *pRet);
void InvokeDelegateInvokeMethod(MethodDesc *pMDDelegateInvoke, int8_t *pArgs, int8_t *pRet, PCODE target, Object** pContinuationRet);
extern "C" PCODE CID_VirtualOpenDelegateDispatch(TransitionBlock * pTransitionBlock);

// Filter to ignore SEH exceptions representing C++ exceptions.
LONG IgnoreCppExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo, PVOID pv)

Review comment (bug):
The global InterpDispatchCache variable g_InterpDispatchCache lacks explicit initialization. While C++ zero-initializes global variables (so m_cache will be all nullptr and m_pDeadList will be nullptr), it would be more explicit and safer to add a constructor to InterpDispatchCache that initializes these members, similar to how DispatchCache has an explicit constructor. This makes the initialization contract clear and prevents potential issues if the struct is used in other contexts.

Code after:
        return hash;
    }

    void ClearEntriesForLoaderAllocator(LoaderAllocator* pLoaderAllocator)
    {
        LIMITED_METHOD_CONTRACT;

        for (size_t i = 0; i < DISPATCH_CACHE_SIZE; i++)
        {
            InterpDispatchCacheEntry* pEntry = VolatileLoadWithoutBarrier(&m_cache[i]);
            if (pEntry == nullptr)
                continue;

            if (pEntry->pMT->GetLoaderAllocator() == pLoaderAllocator ||
                pEntry->pTargetMD->GetLoaderAllocator() == pLoaderAllocator)
            {
                VolatileStoreWithoutBarrier(&m_cache[i], (InterpDispatchCacheEntry*)nullptr);
                // Given the EE is suspended, we can free the entry without worrying about races
                delete pEntry;
            }
        }
    }
};

// Global interpreter dispatch cache instance
static InterpDispatchCache g_InterpDispatchCache;

// Called during GC, when we are guaranteed no entry is being used by any thread.
void InterpDispatchCache_ReclaimAll()
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_COOPERATIVE;
        // Should only be called during a GC suspension
        PRECONDITION(Debug_IsLockedViaThreadSuspension());
    }
    CONTRACTL_END;

    g_InterpDispatchCache.ReclaimDeadEntries();
}

// Clear entries that reference types/methods from the given LoaderAllocator.
// Called during collectible assembly unload when the EE is suspended.
void InterpDispatchCache_ClearForLoaderAllocator(LoaderAllocator* pLoaderAllocator)
{
    g_InterpDispatchCache.ClearEntriesForLoaderAllocator(pLoaderAllocator);
}

static size_t CreateDispatchTokenForMethod(MethodDesc* pMD)