diff options
Diffstat (limited to 'src/vm/classcompat.cpp')
-rw-r--r-- | src/vm/classcompat.cpp | 3710 |
1 files changed, 3710 insertions, 0 deletions
diff --git a/src/vm/classcompat.cpp b/src/vm/classcompat.cpp new file mode 100644 index 0000000000..ac819941f9 --- /dev/null +++ b/src/vm/classcompat.cpp @@ -0,0 +1,3710 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// =========================================================================== +// File: CLASSCOMPAT.CPP + +// =========================================================================== +// This file contains backward compatibility functionality for COM Interop. +// =========================================================================== +// + + +#include "common.h" + +#ifndef DACCESS_COMPILE + +#include "clsload.hpp" +#include "method.hpp" +#include "class.h" +#include "classcompat.h" +#include "object.h" +#include "field.h" +#include "util.hpp" +#include "excep.h" +#include "threads.h" +#include "stublink.h" +#include "dllimport.h" +#include "verifier.hpp" +#include "jitinterface.h" +#include "eeconfig.h" +#include "log.h" +#include "fieldmarshaler.h" +#include "cgensys.h" +#include "gc.h" +#include "security.h" +#include "dbginterface.h" +#include "comdelegate.h" +#include "sigformat.h" +#ifdef FEATURE_REMOTING +#include "remoting.h" +#endif +#include "eeprofinterfaces.h" +#include "dllimportcallback.h" +#include "listlock.h" +#include "methodimpl.h" +#include "guidfromname.h" +#include "stackprobe.h" +#include "encee.h" +#include "encee.h" +#include "comsynchronizable.h" +#include "customattribute.h" +#include "virtualcallstub.h" +#include "eeconfig.h" +#include "contractimpl.h" +#include "prettyprintsig.h" + +#include "comcallablewrapper.h" +#include "clrtocomcall.h" +#include "runtimecallablewrapper.h" + +#include "listlock.inl" +#include "generics.h" +#include "contractimpl.h" + +////////////////////////////////////////////////////////////////////////////////////////////// +ClassCompat::InterfaceInfo_t* InteropMethodTableData::FindInterface(MethodTable *pInterface) +{ + WRAPPER_NO_CONTRACT; + + for (DWORD i = 0; i < cInterfaceMap; i++) + { + ClassCompat::InterfaceInfo_t *iMap = &pInterfaceMap[i]; + if (iMap->m_pMethodTable->IsEquivalentTo(pInterface)) + { + // Extensible RCW's need to be handled specially because they can have interfaces + // in their map that are added at runtime. These interfaces will have a start offset + // of -1 to indicate this. We cannot take for granted that every instance of this + // COM object has this interface so FindInterface on these interfaces is made to fail. + // + // However, we are only considering the statically available slots here + // (m_wNumInterface doesn't contain the dynamic slots), so we can safely + // ignore this detail. + return iMap; + } + } + + return NULL; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// get start slot for interface +// returns -1 if interface not found +WORD InteropMethodTableData::GetStartSlotForInterface(MethodTable* pInterface) +{ + WRAPPER_NO_CONTRACT; + + ClassCompat::InterfaceInfo_t* pInfo = FindInterface(pInterface); + + if (pInfo != NULL) + { + WORD startSlot = pInfo->GetInteropStartSlot(); + _ASSERTE(startSlot != MethodTable::NO_SLOT); + return startSlot; + } + + return MethodTable::NO_SLOT; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +// This will return the interop slot for pMD in pMT. It will traverse the inheritance tree +// to find a match. +/*static*/ WORD InteropMethodTableData::GetSlotForMethodDesc(MethodTable *pMT, MethodDesc *pMD) +{ + while (pMT) + { + InteropMethodTableData *pData = pMT->LookupComInteropData(); + _ASSERTE(pData); + for (DWORD i = 0; i < pData->cVTable; i++) + { + if (pData->pVTable[i].pMD == pMD) + return (WORD) i; + } + pMT = pMT->GetParentMethodTable(); + } + + return MethodTable::NO_SLOT; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +InteropMethodTableSlotDataMap::InteropMethodTableSlotDataMap(InteropMethodTableSlotData *pSlotData, DWORD cSlotData) +{ + m_pSlotData = pSlotData; + m_cSlotData = cSlotData; + m_iCurSlot = 0; +} + +////////////////////////////////////////////////////////////////////////////////////////////// +InteropMethodTableSlotData *InteropMethodTableSlotDataMap::Exists_Helper(MethodDesc *pMD) +{ + LIMITED_METHOD_CONTRACT; + for (DWORD i = 0; i < m_cSlotData; i++) + { + if (m_pSlotData[i].pDeclMD == pMD) + { + return (&m_pSlotData[i]); + } + } + + return (NULL); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +BOOL InteropMethodTableSlotDataMap::Exists(MethodDesc *pMD) +{ + return (Exists_Helper(pMD) != NULL); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +InteropMethodTableSlotData *InteropMethodTableSlotDataMap::GetData(MethodDesc *pMD) +{ + LIMITED_METHOD_CONTRACT; + InteropMethodTableSlotData *pEntry = Exists_Helper(pMD); + + if (pEntry) + return pEntry; + + pEntry = GetNewEntry(); + pEntry->pMD = pMD; + pEntry->pDeclMD = pMD; + return (pEntry); +} + +////////////////////////////////////////////////////////////////////////////////////////////// +InteropMethodTableSlotData *InteropMethodTableSlotDataMap::GetNewEntry() +{ + WRAPPER_NO_CONTRACT; + _ASSERTE(m_iCurSlot < m_cSlotData); + InteropMethodTableSlotData *pEntry = &m_pSlotData[m_iCurSlot++]; + pEntry->pMD = NULL; + pEntry->wFlags = 0; + pEntry->wSlot = MethodTable::NO_SLOT; + pEntry->pDeclMD = NULL; + return (pEntry); +} + +namespace ClassCompat +{ + +////////////////////////////////////////////////////////////////////////////////////////////// +InteropMethodTableData *MethodTableBuilder::BuildInteropVTable(AllocMemTracker *pamTracker) +{ + CONTRACTL { + STANDARD_VM_CHECK; + INSTANCE_CHECK; + } CONTRACTL_END; + + MethodTable * pThisMT = GetHalfBakedMethodTable(); + + // This should never be called for interfaces or for generic types. + _ASSERTE(!pThisMT->IsInterface()); + _ASSERTE(!pThisMT->ContainsGenericVariables()); + _ASSERTE(!pThisMT->HasGenericClassInstantiationInHierarchy()); + + // Array method tables are created quite differently + if (pThisMT->IsArray()) + return BuildInteropVTableForArray(pamTracker); + +#ifdef _DEBUG + BOOL fDump = FALSE; + LPCUTF8 fullName = pThisMT->GetDebugClassName(); + if (fullName) { + LPWSTR wszRegName = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnInteropVTableBuild); + if (wszRegName) { + { // Poor man's narrow + LPWSTR fromPtr = wszRegName; + LPUTF8 toPtr = (LPUTF8) wszRegName; + LPUTF8 result = toPtr; + while(*fromPtr != 0) + *toPtr++ = (char) *fromPtr++; + *toPtr = 0; + } + LPCUTF8 regName = (LPCUTF8) wszRegName; + LPCUTF8 bracket = (LPCUTF8) strchr(fullName, '['); + size_t len = strlen(fullName); + if (bracket != NULL) + len = bracket - fullName; + if (strncmp(fullName, regName, len) == 0) { + _ASSERTE(!"BreakOnInteropVTableBuild"); + fDump = TRUE; + } + delete [] wszRegName; + } + } +#endif // _DEBUG + + //Get Check Point for the thread-based allocator + Thread *pThread = GetThread(); + CheckPointHolder cph(pThread->m_MarshalAlloc.GetCheckpoint()); //hold checkpoint for autorelease + + HRESULT hr = S_OK; + BaseDomain *bmtDomain = pThisMT->GetDomain(); + Module *pModule = pThisMT->GetModule(); + mdToken cl = pThisMT->GetCl(); + MethodTable *pParentMethodTable = pThisMT->GetParentMethodTable(); + + // The following structs, defined as private members of MethodTableBuilder, contain the necessary local + // parameters needed for MethodTableBuilder + + // Look at the struct definitions for a detailed list of all parameters available + // to MethodTableBuilder. + + bmtErrorInfo bmtError; + bmtProperties bmtProp; + bmtVtable bmtVT; + bmtParentInfo bmtParent; + bmtInterfaceInfo bmtInterface; + bmtMethodInfo bmtMethod(pModule->GetMDImport()); + bmtTypeInfo bmtType; + bmtMethodImplInfo bmtMethodImpl(pModule->GetMDImport()); + + //Initialize structs + + bmtError.resIDWhy = IDS_CLASSLOAD_GENERAL; // Set the reason and the offending method def. If the method information + bmtError.pThrowable = NULL; + bmtError.pModule = pModule; + bmtError.cl = cl; + + bmtType.pMDImport = pModule->GetMDImport(); + bmtType.pModule = pModule; + bmtType.cl = cl; + + bmtParent.parentSubst = GetHalfBakedMethodTable()->GetSubstitutionForParent(NULL); + if (FAILED(bmtType.pMDImport->GetTypeDefProps( + bmtType.cl, + &(bmtType.dwAttr), + &(bmtParent.token)))) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); + } + + SetBMTData( + bmtDomain, + &bmtError, + &bmtProp, + &bmtVT, + &bmtParent, + &bmtInterface, + &bmtMethod, + &bmtType, + &bmtMethodImpl); + + // Populate the BMT data structures from the attributes of the incoming MT + if (pThisMT->IsValueType()) SetIsValueClass(); + if (pThisMT->IsEnum()) SetEnum(); + if (pThisMT->HasLayout()) SetHasLayout(); + if (pThisMT->IsDelegate()) SetIsDelegate(); + if (pThisMT->IsContextful()) SetContextful(); +#ifdef FEATURE_COMINTEROP + if(pThisMT->GetClass()->IsComClassInterface()) SetIsComClassInterface(); +#endif + + // Populate the interface list - these are allocated on the thread's stacking allocator + //@TODO: This doesn't work for generics - fix if generics will be exposed to COM + BuildingInterfaceInfo_t *pBuildingInterfaceList; + WORD wNumInterfaces; + BuildInteropVTable_InterfaceList(&pBuildingInterfaceList, &wNumInterfaces); + + bmtInterface.wInterfaceMapSize = wNumInterfaces; + + WORD i; + + // Interfaces have a parent class of Object, but we don't really want to inherit all of + // Object's virtual methods, so pretend we don't have a parent class - at the bottom of this + // function we reset the parent method table + if (IsInterface()) + { + pParentMethodTable = NULL; + } + + bmtParent.pParentMethodTable = pParentMethodTable; + + // Com Import classes are special + if (IsComImport() && !IsEnum() && !IsInterface() && !IsValueClass() && !IsDelegate()) + { + _ASSERTE(pParentMethodTable == g_pBaseCOMObject || pThisMT->IsWinRTObjectType()); + _ASSERTE(!(HasLayout())); + + // if the current class is imported + bmtProp.fIsComObjectType = TRUE; + } + + bmtParent.pParentMethodTable = pParentMethodTable; + + if (pParentMethodTable != NULL) + { + if (pParentMethodTable->IsComObjectType()) + { + // if the parent class is of ComObjectType + // so is the child + bmtProp.fIsComObjectType = TRUE; + } + } + + // resolve unresolved interfaces, determine an upper bound on the size of the interface map, + // and determine the size of the largest interface (in # slots) + BuildInteropVTable_ResolveInterfaces(bmtDomain, pBuildingInterfaceList, &bmtType, &bmtInterface, &bmtVT, &bmtParent, bmtError); + + // Enumerate this class's members + EnumerateMethodImpls(); + + // Enumerate this class's members + EnumerateClassMethods(); + + AllocateMethodWorkingMemory(); + + // Allocate the working memory for the interop data + { + //////////////// + // The interop data for the VTable for COM Interop backward compatibility + + // Allocate space to hold on to the MethodDesc for each entry + bmtVT.ppSDVtable = new (&pThread->m_MarshalAlloc) InteropMethodTableSlotData*[bmtVT.dwMaxVtableSize]; + ZeroMemory(bmtVT.ppSDVtable, bmtVT.dwMaxVtableSize * sizeof(InteropMethodTableSlotData*)); + + // Allocate space to hold on to the MethodDesc for each entry + bmtVT.ppSDNonVtable = new (&pThread->m_MarshalAlloc) InteropMethodTableSlotData*[NumDeclaredMethods()]; + ZeroMemory(bmtVT.ppSDNonVtable , sizeof(InteropMethodTableSlotData*)*NumDeclaredMethods()); + + + DWORD cMaxEntries = (bmtVT.dwMaxVtableSize * 2) + (NumDeclaredMethods() * 2); + InteropMethodTableSlotData *pInteropData = new (&pThread->m_MarshalAlloc) InteropMethodTableSlotData[cMaxEntries]; + memset(pInteropData, 0, cMaxEntries * sizeof(InteropMethodTableSlotData)); + + bmtVT.pInteropData = new (&pThread->m_MarshalAlloc) InteropMethodTableSlotDataMap(pInteropData, cMaxEntries); + + // Initialize the map with parent information + if (bmtParent.pParentMethodTable != NULL) + { + InteropMethodTableData *pParentInteropData = bmtParent.pParentMethodTable->LookupComInteropData(); + _ASSERTE(pParentInteropData); + + for ( i = 0; i < pParentInteropData->cVTable; i++) + { + InteropMethodTableSlotData *pParentSlot = &pParentInteropData->pVTable[i]; + InteropMethodTableSlotData *pNewEntry = bmtVT.pInteropData->GetData(pParentSlot->pDeclMD); + pNewEntry->pMD = pParentSlot->pMD; + pNewEntry->pDeclMD = pParentSlot->pDeclMD; + pNewEntry->wFlags = pParentSlot->wFlags; + pNewEntry->wSlot = pParentSlot->wSlot; + + bmtVT.ppSDVtable[i] = pNewEntry; + } + } + } + + // Determine vtable placement for each member in this class + BuildInteropVTable_PlaceMembers(bmtDomain,&bmtType, wNumInterfaces, pBuildingInterfaceList, &bmtMethod, + &bmtError, &bmtProp, &bmtParent, &bmtInterface, &bmtMethodImpl, &bmtVT); + + // First copy what we can leverage from the parent's interface map. + // The parent's interface map will be identical to the beginning of this class's interface map (i.e. + // the interfaces will be listed in the identical order). + if (bmtParent.wNumParentInterfaces > 0) + { + PREFIX_ASSUME(pParentMethodTable != NULL); // We have to have parent to have parent interfaces + + _ASSERTE(pParentMethodTable->LookupComInteropData()); + _ASSERTE(bmtParent.wNumParentInterfaces == pParentMethodTable->LookupComInteropData()->cInterfaceMap); + InterfaceInfo_t *pParentInterfaceList = pParentMethodTable->LookupComInteropData()->pInterfaceMap; + + + for (i = 0; i < bmtParent.wNumParentInterfaces; i++) + { +#ifdef _DEBUG + _ASSERTE(pParentInterfaceList[i].m_pMethodTable == bmtInterface.pInterfaceMap[i].m_pMethodTable); + + MethodTable *pMT = pParentInterfaceList[i].m_pMethodTable; + + // If the interface resides entirely inside the parent's class methods (i.e. no duplicate + // slots), then we can place this interface in an identical spot to in the parent. + // + // Note carefully: the vtable for this interface could start within the first GetNumVirtuals() + // entries, but could actually extend beyond it, if we were particularly efficient at placing + // this interface, so check that the end of the interface vtable is before + // pParentMethodTable->GetNumVirtuals(). + + _ASSERTE(pParentInterfaceList[i].GetInteropStartSlot() + pMT->GetNumVirtuals() <= + pParentMethodTable->LookupComInteropData()->cVTable); +#endif // _DEBUG + // Interface lies inside parent's methods, so we can place it + bmtInterface.pInterfaceMap[i].SetInteropStartSlot(pParentInterfaceList[i].GetInteropStartSlot()); + } + } + + // + // If we are a class, then there may be some unplaced vtable methods (which are by definition + // interface methods, otherwise they'd already have been placed). Place as many unplaced methods + // as possible, in the order preferred by interfaces. However, do not allow any duplicates - once + // a method has been placed, it cannot be placed again - if we are unable to neatly place an interface, + // create duplicate slots for it starting at dwCurrentDuplicateVtableSlot. Fill out the interface + // map for all interfaces as they are placed. + // + // If we are an interface, then all methods are already placed. Fill out the interface map for + // interfaces as they are placed. + // + if (!IsInterface()) + { + BuildInteropVTable_PlaceVtableMethods( + &bmtInterface, + wNumInterfaces, + pBuildingInterfaceList, + &bmtVT, + &bmtMethod, + &bmtType, + &bmtError, + &bmtProp, + &bmtParent); + + BuildInteropVTable_PlaceMethodImpls( + bmtDomain, + &bmtType, + &bmtMethodImpl, + &bmtError, + &bmtInterface, + &bmtVT, + &bmtParent); + } + +#ifdef _DEBUG + if (IsInterface() == FALSE) + { + for (i = 0; i < bmtInterface.wInterfaceMapSize; i++) + { + _ASSERTE(bmtInterface.pInterfaceMap[i].GetInteropStartSlot() != MethodTable::NO_SLOT); + } + } +#endif // _DEBUG + + // Place all non vtable methods + for (i = 0; i < bmtVT.wCurrentNonVtableSlot; i++) + { + bmtVT.SetMethodDescForSlot(bmtVT.wCurrentVtableSlot + i, bmtVT.ppSDNonVtable[i]->pMD); + CONSISTENCY_CHECK(bmtVT.ppSDNonVtable[i]->wSlot != MethodTable::NO_SLOT); + bmtVT.ppSDVtable[bmtVT.wCurrentVtableSlot + i] = bmtVT.ppSDNonVtable[i]; + } + + // Must copy overridden slots to duplicate entries in the vtable + BuildInteropVTable_PropagateInheritance(&bmtVT); + + // ensure we didn't overflow the temporary vtable + _ASSERTE(bmtVT.wCurrentNonVtableSlot <= bmtVT.dwMaxVtableSize); + + // Finalize. + InteropMethodTableData *pInteropMT = NULL; + + FinalizeInteropVTable( + pamTracker, + pThisMT->GetLoaderAllocator(), + &bmtVT, + &bmtInterface, + &bmtType, + &bmtProp, + &bmtMethod, + &bmtError, + &bmtParent, + &pInteropMT); + _ASSERTE(pInteropMT); + +#ifdef _DEBUG + if (fDump) + { + CQuickBytes qb; + DWORD cb = 0; + PCCOR_SIGNATURE pSig; + ULONG cbSig; + + printf("InteropMethodTable\n--------------\n"); + printf("VTable\n------\n"); + + for (DWORD i = 0; i < pInteropMT->cVTable; i++) + { + // Print the method name + InteropMethodTableSlotData *pInteropMD = &pInteropMT->pVTable[i]; + printf(pInteropMD->pMD->GetName()); + printf(" "); + + // Print the sig + if (FAILED(pInteropMD->pMD->GetMDImport()->GetSigOfMethodDef(pInteropMD->pMD->GetMemberDef(), &cbSig, &pSig))) + { + pSig = NULL; + cbSig = 0; + } + PrettyPrintSigInternalLegacy(pSig, cbSig, "", &qb, pInteropMD->pMD->GetMDImport()); + printf((LPCUTF8) qb.Ptr()); + printf("\n"); + } + } +#endif // _DEBUG + + NullBMTData(); + + return pInteropMT; +} + +//--------------------------------------------------------------------------------------- +InteropMethodTableData *MethodTableBuilder::BuildInteropVTableForArray(AllocMemTracker *pamTracker) +{ + CONTRACTL { + STANDARD_VM_CHECK; + INSTANCE_CHECK; + PRECONDITION(GetHalfBakedMethodTable()->IsArray()); + PRECONDITION(GetHalfBakedMethodTable()->GetNumVirtuals() == GetHalfBakedMethodTable()->GetParentMethodTable()->GetNumVirtuals()); + } CONTRACTL_END; + + MethodTable * pThisMT = GetHalfBakedMethodTable(); + + // Get the interop data for the parent + MethodTable *pParentMT = pThisMT->GetParentMethodTable(); + InteropMethodTableData *pParentMTData = pParentMT->GetComInteropData(); + CONSISTENCY_CHECK(pParentMTData != NULL); + + // Allocate in the same heap as the array itself + LoaderHeap *pHeap = pThisMT->GetLoaderAllocator()->GetLowFrequencyHeap(); + + // Allocate the overall structure + InteropMethodTableData *pMTData = (InteropMethodTableData *)(void *) pamTracker->Track(pHeap->AllocMem(S_SIZE_T(sizeof(InteropMethodTableData)))); + memset(pMTData, 0, sizeof(InteropMethodTableData)); + + // Allocate the vtable - this is just a copy from System.Array + pMTData->cVTable = pParentMTData->cVTable; + if (pMTData->cVTable != 0) + { + pMTData->pVTable = (InteropMethodTableSlotData *)(void *) + pamTracker->Track(pHeap->AllocMem(S_SIZE_T(sizeof(InteropMethodTableSlotData)) * S_SIZE_T(pMTData->cVTable))); + + // Copy the vtable + for (DWORD i = 0; i < pMTData->cVTable; i++) + pMTData->pVTable[i] = pParentMTData->pVTable[i]; + } + + // Allocate the non-vtable + pMTData->cNonVTable = pThisMT->GetNumMethods() - pThisMT->GetNumVirtuals(); + if (pMTData->cNonVTable != 0) + { + pMTData->pNonVTable = (InteropMethodTableSlotData *)(void *) + pamTracker->Track(pHeap->AllocMem(S_SIZE_T(sizeof(InteropMethodTableSlotData)) * S_SIZE_T(pMTData->cNonVTable))); + + // Copy the non-vtable + UINT32 iCurRealSlot = pThisMT->GetNumVirtuals(); + WORD iCurInteropSlot = pMTData->cVTable; + for (DWORD i = 0; i < pMTData->cNonVTable; i++, iCurRealSlot++, iCurInteropSlot++) + { + pMTData->pNonVTable[i].wSlot = iCurInteropSlot; + pMTData->pNonVTable[i].pMD = pThisMT->GetMethodDescForSlot(iCurRealSlot); + } + } + + // Allocate the interface map + pMTData->cInterfaceMap = pParentMTData->cInterfaceMap; + if (pMTData->cInterfaceMap != 0) + { + pMTData->pInterfaceMap = (InterfaceInfo_t *)(void *) + pamTracker->Track(pHeap->AllocMem(S_SIZE_T(sizeof(InterfaceInfo_t)) * S_SIZE_T(pMTData->cInterfaceMap))); + + // Copy the interface map + for (DWORD i = 0; i < pMTData->cInterfaceMap; i++) + pMTData->pInterfaceMap[i] = pParentMTData->pInterfaceMap[i]; + } + + return pMTData; +} + +//--------------------------------------------------------------------------------------- +VOID MethodTableBuilder::BuildInteropVTable_InterfaceList( + BuildingInterfaceInfo_t **ppBuildingInterfaceList, + WORD *pcBuildingInterfaceList) +{ + STANDARD_VM_CONTRACT; + + // Initialize arguments + *pcBuildingInterfaceList = 0; + *ppBuildingInterfaceList = NULL; + + // Get the thread for stacking allocator + Thread *pThread = GetThread(); + + // Get the metadata for enumerating the interfaces of the class + IMDInternalImport *pMDImport = GetModule()->GetMDImport(); + + // Now load all the interfaces + HENUMInternalHolder hEnumInterfaceImpl(pMDImport); + hEnumInterfaceImpl.EnumInit(mdtInterfaceImpl, GetCl()); + + // Get the count for the number of interfaces from metadata + DWORD cAllInterfaces = pMDImport->EnumGetCount(&hEnumInterfaceImpl); + WORD cNonGenericItfs = 0; + + // Iterate through each interface token and get the type for the interface and put + // it into the BuildingInterfaceInfo_t struct. + if (cAllInterfaces != 0) + { + mdInterfaceImpl ii; + Module *pModule = GetModule(); + + // Allocate the BuildingInterfaceList table + *ppBuildingInterfaceList = new(&pThread->m_MarshalAlloc) BuildingInterfaceInfo_t[cAllInterfaces]; + BuildingInterfaceInfo_t *pInterfaceBuildInfo = *ppBuildingInterfaceList; + + while (pMDImport->EnumNext(&hEnumInterfaceImpl, &ii)) + { + mdTypeRef crInterface; + TypeHandle intType; + + // Get properties on this interface + if (FAILED(pMDImport->GetTypeOfInterfaceImpl(ii, &crInterface))) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); + } + SigTypeContext typeContext = SigTypeContext(TypeHandle(GetHalfBakedMethodTable())); + intType = ClassLoader::LoadTypeDefOrRefOrSpecThrowing(pModule, crInterface, &typeContext, + ClassLoader::ThrowIfNotFound, + ClassLoader::FailIfUninstDefOrRef); + + // At this point, the interface should never have any non instantiated generic parameters. + _ASSERTE(!intType.IsGenericTypeDefinition()); + + // Skip any generic interfaces. + if (intType.GetNumGenericArgs() != 0) + continue; + + pInterfaceBuildInfo[cNonGenericItfs].m_pMethodTable = intType.AsMethodTable(); + _ASSERTE(pInterfaceBuildInfo[cNonGenericItfs].m_pMethodTable != NULL); + _ASSERTE(pInterfaceBuildInfo[cNonGenericItfs].m_pMethodTable->IsInterface()); + cNonGenericItfs++; + } + _ASSERTE(cNonGenericItfs <= cAllInterfaces); + } + + *pcBuildingInterfaceList = cNonGenericItfs; +} + +//--------------------------------------------------------------------------------------- +// Used by BuildInteropVTable +// +// Determine vtable placement for each member in this class +// + +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +VOID MethodTableBuilder::BuildInteropVTable_PlaceMembers( + BaseDomain *bmtDomain, + bmtTypeInfo* bmtType, + DWORD numDeclaredInterfaces, + BuildingInterfaceInfo_t *pBuildingInterfaceList, + bmtMethodInfo* bmtMethod, + bmtErrorInfo* bmtError, + bmtProperties* bmtProp, + bmtParentInfo* bmtParent, + bmtInterfaceInfo* bmtInterface, + bmtMethodImplInfo* bmtMethodImpl, + bmtVtable* bmtVT) +{ + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(this)); + PRECONDITION(CheckPointer(bmtType)); + PRECONDITION(CheckPointer(bmtMethod)); + PRECONDITION(CheckPointer(bmtError)); + PRECONDITION(CheckPointer(bmtProp)); + PRECONDITION(CheckPointer(bmtInterface)); + PRECONDITION(CheckPointer(bmtParent)); + PRECONDITION(CheckPointer(bmtMethodImpl)); + PRECONDITION(CheckPointer(bmtVT)); + } + CONTRACTL_END; + + _ASSERTE(!IsInterface()); + + Module * pModule = GetModule(); + +#ifdef _DEBUG + LPCUTF8 pszDebugName,pszDebugNamespace; + if (FAILED(bmtType->pModule->GetMDImport()->GetNameOfTypeDef(GetCl(), &pszDebugName, &pszDebugNamespace))) + { + pszDebugName = pszDebugNamespace = "Invalid TypeDef record"; + } +#endif // _DEBUG + + HRESULT hr = S_OK; + DWORD i, j; + DWORD dwClassDeclFlags = 0xffffffff; + DWORD dwClassNullDeclFlags = 0xffffffff; + + for (i = 0; i < NumDeclaredMethods(); i++) + { + LPCUTF8 szMemberName = NULL; + PCCOR_SIGNATURE pMemberSignature = NULL; + DWORD cMemberSignature = 0; + mdToken tokMember; + DWORD dwMemberAttrs; + DWORD dwDescrOffset; + DWORD dwImplFlags; + BOOL fMethodImplementsInterface = FALSE; + DWORD dwMDImplementsInterfaceNum = 0; + DWORD dwMDImplementsSlotNum = 0; + DWORD dwParentAttrs; + + tokMember = bmtMethod->rgMethodTokens[i]; + dwMemberAttrs = bmtMethod->rgMethodAttrs[i]; + dwDescrOffset = bmtMethod->rgMethodRVA[i]; + dwImplFlags = bmtMethod->rgMethodImplFlags[i]; + + DWORD Classification = bmtMethod->rgMethodClassifications[i]; + + // If this member is a method which overrides a parent method, it will be set to non-NULL + MethodDesc *pParentMethodDesc = NULL; + + szMemberName = bmtMethod->rgszMethodName[i]; + + // constructors and class initialisers are special + if (!IsMdRTSpecialName(dwMemberAttrs)) + { + // The method does not have the special marking + if (IsMdVirtual(dwMemberAttrs)) + { + // Hash that a method with this name exists in this class + // Note that ctors and static ctors are not added to the table + DWORD dwHashName = HashStringA(szMemberName); + + // If the member is marked with a new slot we do not need to find it + // in the parent + if (!IsMdNewSlot(dwMemberAttrs)) + { + // If we're not doing sanity checks, then assume that any method declared static + // does not attempt to override some virtual parent. + if (!IsMdStatic(dwMemberAttrs) && bmtParent->pParentMethodTable != NULL) + { + // Attempt to find the method with this name and signature in the parent class. + // This method may or may not create pParentMethodHash (if it does not already exist). + // It also may or may not fill in pMemberSignature/cMemberSignature. + // An error is only returned when we can not create the hash. + // NOTE: This operation touches metadata + { + BOOL fMethodConstraintsMatch = FALSE; + VERIFY(SUCCEEDED(LoaderFindMethodInClass( + szMemberName, + bmtType->pModule, + tokMember, + &pParentMethodDesc, + &pMemberSignature, &cMemberSignature, + dwHashName, + &fMethodConstraintsMatch))); + //this assert should hold because interop methods cannot be generic + _ASSERTE(pParentMethodDesc == NULL || fMethodConstraintsMatch); + } + + if (pParentMethodDesc != NULL) + { + dwParentAttrs = pParentMethodDesc->GetAttrs(); + + _ASSERTE(IsMdVirtual(dwParentAttrs) && "Non virtual methods should not be searched"); + _ASSERTE(!(IsMdFinal(dwParentAttrs))); + } + } + } + } + } + + if(pParentMethodDesc == NULL) { + // This method does not exist in the parent. If we are a class, check whether this + // method implements any interface. If true, we can't place this method now. + if ((!IsInterface()) && + ( IsMdPublic(dwMemberAttrs) && + IsMdVirtual(dwMemberAttrs) && + !IsMdStatic(dwMemberAttrs) && + !IsMdRTSpecialName(dwMemberAttrs))) { + + // Don't check parent class interfaces - if the parent class had to implement an interface, + // then it is already guaranteed that we inherited that method. + _ASSERTE(!bmtParent->pParentMethodTable || bmtParent->pParentMethodTable->LookupComInteropData()); + DWORD numInheritedInts = (bmtParent->pParentMethodTable ? + (DWORD) bmtParent->pParentMethodTable->LookupComInteropData()->cInterfaceMap: 0); + + for (j = numInheritedInts; j < bmtInterface->wInterfaceMapSize; j++) + { + MethodTable *pInterface = bmtInterface->pInterfaceMap[j].m_pMethodTable; + if (pMemberSignature == NULL) + { // We've been trying to avoid asking for the signature - now we need it + if (FAILED(bmtType->pMDImport->GetSigOfMethodDef(tokMember, &cMemberSignature, &pMemberSignature))) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); + } + } + + WORD slotNum = (WORD) (-1); + MethodDesc *pItfMD = MemberLoader::FindMethod(pInterface, + szMemberName, pMemberSignature, cMemberSignature, bmtType->pModule); + + if (pItfMD != NULL) + { + // This method implements an interface - don't place it + fMethodImplementsInterface = TRUE; + + // Keep track of this fact and use it while placing the interface + slotNum = (WORD) pItfMD->GetSlot(); + if (bmtInterface->pppInterfaceImplementingMD[j] == NULL) + { + Thread *pThread = GetThread(); + StackingAllocator * pAlloc = &pThread->m_MarshalAlloc; + + bmtInterface->pppInterfaceImplementingMD[j] = new (pAlloc) MethodDesc * [pInterface->GetNumVirtuals()]; + memset(bmtInterface->pppInterfaceImplementingMD[j], 0, sizeof(MethodDesc *) * pInterface->GetNumVirtuals()); + + bmtInterface->pppInterfaceDeclaringMD[j] = new (pAlloc) MethodDesc * [pInterface->GetNumVirtuals()]; + memset(bmtInterface->pppInterfaceDeclaringMD[j], 0, sizeof(MethodDesc *) * pInterface->GetNumVirtuals()); + } + + bmtInterface->pppInterfaceDeclaringMD[j][slotNum] = pItfMD; + + dwMDImplementsInterfaceNum = j; + dwMDImplementsSlotNum = slotNum; + break; + } + } + } + } + + // Now find the MethodDesc associated with this method + MethodDesc *pNewMD = MemberLoader::FindMethod(GetHalfBakedMethodTable(), tokMember); + _ASSERTE(!bmtVT->pInteropData->Exists(pNewMD)); + InteropMethodTableSlotData *pNewMDData = bmtVT->pInteropData->GetData(pNewMD); + + _ASSERTE(pNewMD != NULL); + _ASSERTE(dwMemberAttrs == pNewMD->GetAttrs()); + + _ASSERTE(bmtParent->ppParentMethodDescBufPtr != NULL); + _ASSERTE(((bmtParent->ppParentMethodDescBufPtr - bmtParent->ppParentMethodDescBuf) / sizeof(MethodDesc*)) + < NumDeclaredMethods()); + *(bmtParent->ppParentMethodDescBufPtr++) = pParentMethodDesc; + *(bmtParent->ppParentMethodDescBufPtr++) = pNewMD; + + if (fMethodImplementsInterface && IsMdVirtual(dwMemberAttrs)) + { + bmtInterface->pppInterfaceImplementingMD[dwMDImplementsInterfaceNum][dwMDImplementsSlotNum] = pNewMD; + } + + // Set the MethodDesc value + bmtMethod->ppMethodDescList[i] = pNewMD; + + // Make sure that fcalls have a 0 rva. This is assumed by the prejit fixup logic + _ASSERTE(((Classification & ~mdcMethodImpl) != mcFCall) || dwDescrOffset == 0); + + // Non-virtual method + if (IsMdStatic(dwMemberAttrs) || + !IsMdVirtual(dwMemberAttrs) || + IsMdRTSpecialName(dwMemberAttrs)) + { + // Non-virtual method (doesn't go into the vtable) + _ASSERTE(bmtVT->pNonVtableMD[bmtVT->wCurrentNonVtableSlot] == NULL); + + // Set the data for the method + pNewMDData->wSlot = bmtVT->wCurrentNonVtableSlot; + + // Add the slot into the non-virtual method table + bmtVT->pNonVtableMD[bmtVT->wCurrentNonVtableSlot] = pNewMD; + bmtVT->ppSDNonVtable[bmtVT->wCurrentNonVtableSlot] = pNewMDData; + + // Increment the current non-virtual method table slot + bmtVT->wCurrentNonVtableSlot++; + } + + // Virtual method + else + { + if (IsInterface()) + { // (shouldn't happen for this codepath) + UNREACHABLE(); + } + + else if (pParentMethodDesc != NULL) + { // We are overriding a parent's vtable slot + CONSISTENCY_CHECK(bmtVT->pInteropData->Exists(pParentMethodDesc)); + WORD slotNumber = bmtVT->pInteropData->GetData(pParentMethodDesc)->wSlot; + + // If the MethodDesc was inherited by an interface but not implemented, + // then the interface's MethodDesc is sitting in the slot and will not reflect + // the true slot number. Need to find the starting slot of the interface in + // the parent class to figure out the true slot (starting slot + itf slot) + if (pParentMethodDesc->IsInterface()) + { + MethodTable *pItfMT = pParentMethodDesc->GetMethodTable(); + WORD startSlot = bmtParent->pParentMethodTable->LookupComInteropData()->GetStartSlotForInterface(pItfMT); + _ASSERTE(startSlot != (WORD) -1); + slotNumber += startSlot; + } + + // we are overriding a parent method, so place this method now + bmtVT->SetMethodDescForSlot(slotNumber, pNewMD); + bmtVT->ppSDVtable[slotNumber] = pNewMDData; + + pNewMDData->wSlot = slotNumber; + } + + else if (!fMethodImplementsInterface) + { // Place it unless we will do it when laying out an interface or it is a body to + // a method impl. If it is an impl then we will use the slots used by the definition. + + // Store the slot for this method + pNewMDData->wSlot = bmtVT->wCurrentVtableSlot; + + // Now copy the method into the vtable, and interop data + bmtVT->SetMethodDescForSlot(bmtVT->wCurrentVtableSlot, pNewMD); + bmtVT->ppSDVtable[bmtVT->wCurrentVtableSlot] = pNewMDData; + + // Increment current vtable slot, since we're not overriding a parent slot + bmtVT->wCurrentVtableSlot++; + } + } + + if(Classification & mdcMethodImpl) + { // If this method serves as the BODY of a MethodImpl specification, then + // we should iterate all the MethodImpl's for this class and see just how many + // of them this method participates in as the BODY. + for(DWORD m = 0; m < bmtMethodImpl->dwNumberMethodImpls; m++) + { + if(tokMember == bmtMethodImpl->rgMethodImplTokens[m].methodBody) + { + MethodDesc* desc = NULL; + BOOL fIsMethod; + mdToken mdDecl = bmtMethodImpl->rgMethodImplTokens[m].methodDecl; + Substitution *pDeclSubst = &bmtMethodImpl->pMethodDeclSubsts[m]; + + // Get the parent + mdToken tkParent = mdTypeDefNil; + if (TypeFromToken(mdDecl) == mdtMethodDef || TypeFromToken(mdDecl) == mdtMemberRef) + { + hr = bmtType->pMDImport->GetParentToken(mdDecl,&tkParent); + if (FAILED(hr)) + { + BuildMethodTableThrowException(hr, *bmtError); + } + } + + if (GetCl() == tkParent) + { // The DECL has been declared + // within the class that we're currently building. + hr = S_OK; + + if(bmtError->pThrowable != NULL) + *(bmtError->pThrowable) = NULL; + + // <TODO>Verify that the substitution doesn't change for this case </TODO> + if(TypeFromToken(mdDecl) != mdtMethodDef) { + hr = FindMethodDeclarationForMethodImpl( + bmtType->pMDImport, + GetCl(), + mdDecl, + &mdDecl); + _ASSERTE(SUCCEEDED(hr)); + + // Make sure the virtual states are the same + DWORD dwDescAttrs; + if (FAILED(bmtType->pMDImport->GetMethodDefProps(mdDecl, &dwDescAttrs))) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); + } + _ASSERTE(IsMdVirtual(dwMemberAttrs) == IsMdVirtual(dwDescAttrs)); + } + } + else + { + SigTypeContext typeContext; + + desc = MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec(bmtType->pModule, + mdDecl, + &typeContext, + FALSE, FALSE); // don't demand generic method args + mdDecl = mdTokenNil; + // Make sure the body is virtaul + _ASSERTE(IsMdVirtual(dwMemberAttrs)); + } + + // Only add the method impl if the interface it is declared on is non generic. + // NULL desc represent method impls to methods on the current class which + // we know isn't generic. + if ((desc == NULL) || (desc->GetMethodTable()->GetNumGenericArgs() == 0)) + { + bmtMethodImpl->AddMethod(pNewMD, + desc, + mdDecl, + pDeclSubst); + } + } + } + } + } /* end ... for each member */ +} + +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +//--------------------------------------------------------------------------------------- +// Resolve unresolved interfaces, determine an upper bound on the size of the interface map, +// and determine the size of the largest interface (in # slots) +VOID MethodTableBuilder::BuildInteropVTable_ResolveInterfaces( + BaseDomain *bmtDomain, + BuildingInterfaceInfo_t *pBuildingInterfaceList, + bmtTypeInfo* bmtType, + bmtInterfaceInfo* bmtInterface, + bmtVtable* bmtVT, + bmtParentInfo* bmtParent, + const bmtErrorInfo & bmtError) +{ + + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(this)); + PRECONDITION(CheckPointer(bmtDomain)); + PRECONDITION(CheckPointer(bmtInterface)); + PRECONDITION(CheckPointer(bmtVT)); + PRECONDITION(CheckPointer(bmtParent)); + } + CONTRACTL_END; + + HRESULT hr = S_OK; + DWORD i; + Thread *pThread = GetThread(); + + // resolve unresolved interfaces, determine an upper bound on the size of the interface map, + // and determine the size of the largest interface (in # slots) + bmtInterface->dwMaxExpandedInterfaces = 0; // upper bound on max # interfaces implemented by this class + + // First look through the interfaces explicitly declared by this class + for (i = 0; i < bmtInterface->wInterfaceMapSize; i++) + { + MethodTable *pInterface = pBuildingInterfaceList[i].m_pMethodTable; + + bmtInterface->dwMaxExpandedInterfaces += (1+ pInterface->GetNumInterfaces()); + } + + // Now look at interfaces inherited from the parent + if (bmtParent->pParentMethodTable != NULL) + { + _ASSERTE(bmtParent->pParentMethodTable->LookupComInteropData()); + InteropMethodTableData *pInteropData = bmtParent->pParentMethodTable->LookupComInteropData(); + InterfaceInfo_t *pParentInterfaceMap = pInteropData->pInterfaceMap; + + for (i = 0; i < pInteropData->cInterfaceMap; i++) + { + MethodTable *pInterface = pParentInterfaceMap[i].m_pMethodTable; + + bmtInterface->dwMaxExpandedInterfaces += (1+pInterface->GetNumInterfaces()); + } + } + + // Create a fully expanded map of all interfaces we implement + bmtInterface->pInterfaceMap = new (&pThread->m_MarshalAlloc) InterfaceInfo_t[bmtInterface->dwMaxExpandedInterfaces]; + + // # slots of largest interface + bmtInterface->dwLargestInterfaceSize = 0; + + DWORD dwNumDeclaredInterfaces = bmtInterface->wInterfaceMapSize; + + BuildInteropVTable_CreateInterfaceMap(pBuildingInterfaceList, bmtInterface, &bmtInterface->wInterfaceMapSize, &bmtInterface->dwLargestInterfaceSize, bmtParent->pParentMethodTable); + + _ASSERTE(bmtInterface->wInterfaceMapSize <= bmtInterface->dwMaxExpandedInterfaces); + + if (bmtInterface->dwLargestInterfaceSize > 0) + { + // This is needed later - for each interface, we get the MethodDesc pointer for each + // method. We need to be able to persist at most one interface at a time, so we + // need enough memory for the largest interface. + bmtInterface->ppInterfaceMethodDescList = new (&pThread->m_MarshalAlloc) MethodDesc*[bmtInterface->dwLargestInterfaceSize]; + + bmtInterface->ppInterfaceDeclMethodDescList = new (&pThread->m_MarshalAlloc) MethodDesc*[bmtInterface->dwLargestInterfaceSize]; + } + + EEClass *pParentClass = (IsInterface() || bmtParent->pParentMethodTable == NULL) ? NULL : bmtParent->pParentMethodTable->GetClass(); + + // For all the new interfaces we bring in, sum the methods + bmtInterface->dwTotalNewInterfaceMethods = 0; + if (pParentClass != NULL) + { + for (i = bmtParent->pParentMethodTable->GetNumInterfaces(); i < (bmtInterface->wInterfaceMapSize); i++) + bmtInterface->dwTotalNewInterfaceMethods += + bmtInterface->pInterfaceMap[i].m_pMethodTable->GetNumVirtuals(); + } + + // The interface map is probably smaller than dwMaxExpandedInterfaces, so we'll copy the + // appropriate number of bytes when we allocate the real thing later. + + // Inherit parental slot counts + if (pParentClass != NULL) + { + InteropMethodTableData *pParentInteropMT = bmtParent->pParentMethodTable->LookupComInteropData(); + bmtVT->wCurrentVtableSlot = pParentInteropMT->cVTable; + bmtParent->wNumParentInterfaces = pParentInteropMT->cInterfaceMap; + } + else + { + bmtVT->wCurrentVtableSlot = 0; + bmtParent->wNumParentInterfaces = 0; + } + + bmtVT->wCurrentNonVtableSlot = 0; + + bmtInterface->pppInterfaceImplementingMD = (MethodDesc ***) pThread->m_MarshalAlloc.Alloc(S_UINT32(sizeof(MethodDesc *)) * S_UINT32(bmtInterface->dwMaxExpandedInterfaces)); + memset(bmtInterface->pppInterfaceImplementingMD, 0, sizeof(MethodDesc *) * bmtInterface->dwMaxExpandedInterfaces); + + bmtInterface->pppInterfaceDeclaringMD = (MethodDesc ***) pThread->m_MarshalAlloc.Alloc(S_UINT32(sizeof(MethodDesc *)) * S_UINT32(bmtInterface->dwMaxExpandedInterfaces)); + memset(bmtInterface->pppInterfaceDeclaringMD, 0, sizeof(MethodDesc *) * bmtInterface->dwMaxExpandedInterfaces); + + return; + +} + +//--------------------------------------------------------------------------------------- +// Fill out a fully expanded interface map, such that if we are declared to implement I3, and I3 extends I1,I2, +// then I1,I2 are added to our list if they are not already present. +// +// Returns FALSE for failure. <TODO>Currently we don't fail, but @TODO perhaps we should fail if we recurse +// too much.</TODO> +// +VOID MethodTableBuilder::BuildInteropVTable_CreateInterfaceMap(BuildingInterfaceInfo_t *pBuildingInterfaceList, + bmtInterfaceInfo* bmtInterface, + WORD *pwInterfaceListSize, + DWORD *pdwMaxInterfaceMethods, + MethodTable *pParentMethodTable) +{ + STANDARD_VM_CONTRACT; + + WORD i; + InterfaceInfo_t *pInterfaceMap = bmtInterface->pInterfaceMap; + WORD wNumInterfaces = bmtInterface->wInterfaceMapSize; + + // pdwInterfaceListSize points to bmtInterface->pInterfaceMapSize so we cache it above + *pwInterfaceListSize = 0; + + // First inherit all the parent's interfaces. This is important, because our interface map must + // list the interfaces in identical order to our parent. + // + // <NICE> we should document the reasons why. One reason is that DispatchMapTypeIDs can be indexes + // into the list </NICE> + if (pParentMethodTable != NULL) + { + _ASSERTE(pParentMethodTable->LookupComInteropData()); + InteropMethodTableData *pInteropData = pParentMethodTable->LookupComInteropData(); + InterfaceInfo_t *pParentInterfaceMap = pInteropData->pInterfaceMap; + unsigned cParentInterfaceMap = pInteropData->cInterfaceMap; + + // The parent's interface list is known to be fully expanded + for (i = 0; i < cParentInterfaceMap; i++) + { + // Need to keep track of the interface with the largest number of methods + if (pParentInterfaceMap[i].m_pMethodTable->GetNumVirtuals() > *pdwMaxInterfaceMethods) + { + *pdwMaxInterfaceMethods = pParentInterfaceMap[i].m_pMethodTable->GetNumVirtuals(); + } + + pInterfaceMap[*pwInterfaceListSize].m_pMethodTable = pParentInterfaceMap[i].m_pMethodTable; + pInterfaceMap[*pwInterfaceListSize].SetInteropStartSlot(MethodTable::NO_SLOT); + pInterfaceMap[*pwInterfaceListSize].m_wFlags = 0; + (*pwInterfaceListSize)++; + } + } + + // Go through each interface we explicitly implement (if a class), or extend (if an interface) + for (i = 0; i < wNumInterfaces; i++) + { + MethodTable *pDeclaredInterface = pBuildingInterfaceList[i].m_pMethodTable; + + BuildInteropVTable_ExpandInterface(pInterfaceMap, pDeclaredInterface, + pwInterfaceListSize, pdwMaxInterfaceMethods, + TRUE); + } +} + +//--------------------------------------------------------------------------------------- +// Given an interface map to fill out, expand pNewInterface (and its sub-interfaces) into it, increasing +// pdwInterfaceListSize as appropriate, and avoiding duplicates. +// +VOID MethodTableBuilder::BuildInteropVTable_ExpandInterface(InterfaceInfo_t *pInterfaceMap, + MethodTable *pNewInterface, + WORD *pwInterfaceListSize, + DWORD *pdwMaxInterfaceMethods, + BOOL fDirect) +{ + STANDARD_VM_CONTRACT; + + DWORD i; + + // The interface list contains the fully expanded set of interfaces from the parent then + // we start adding all the interfaces we declare. We need to know which interfaces + // we declare but do not need duplicates of the ones we declare. This means we can + // duplicate our parent entries. + + // Is it already present in the list? + for (i = 0; i < (*pwInterfaceListSize); i++) { + if (pInterfaceMap[i].m_pMethodTable->IsEquivalentTo(pNewInterface)) { + if(fDirect) { + pInterfaceMap[i].m_wFlags |= InterfaceInfo_t::interface_declared_on_class; + } + return; // found it, don't add it again + } + } + + if (pNewInterface->GetNumVirtuals() > *pdwMaxInterfaceMethods) { + *pdwMaxInterfaceMethods = pNewInterface->GetNumVirtuals(); + } + + // Add it and each sub-interface + pInterfaceMap[*pwInterfaceListSize].m_pMethodTable = pNewInterface; + pInterfaceMap[*pwInterfaceListSize].SetInteropStartSlot(MethodTable::NO_SLOT); + pInterfaceMap[*pwInterfaceListSize].m_wFlags = 0; + + if(fDirect) + pInterfaceMap[*pwInterfaceListSize].m_wFlags |= InterfaceInfo_t::interface_declared_on_class; + + (*pwInterfaceListSize)++; + + if (pNewInterface->GetNumInterfaces() != 0) { + MethodTable::InterfaceMapIterator it = pNewInterface->IterateInterfaceMap(); + while (it.Next()) { + BuildInteropVTable_ExpandInterface(pInterfaceMap, it.GetInterface(), + pwInterfaceListSize, pdwMaxInterfaceMethods, FALSE); + } + } + + return; +} + +// If we are a class, then there may be some unplaced vtable methods (which are by definition +// interface methods, otherwise they'd already have been placed). Place as many unplaced methods +// as possible, in the order preferred by interfaces. However, do not allow any duplicates - once +// a method has been placed, it cannot be placed again - if we are unable to neatly place an interface, +// create duplicate slots for it starting at dwCurrentDuplicateVtableSlot. Fill out the interface +// map for all interfaces as they are placed. +// +// If we are an interface, then all methods are already placed. Fill out the interface map for +// interfaces as they are placed. +// + +//--------------------------------------------------------------------------------------- +VOID MethodTableBuilder::BuildInteropVTable_PlaceVtableMethods( + bmtInterfaceInfo* bmtInterface, + DWORD numDeclaredInterfaces, + BuildingInterfaceInfo_t *pBuildingInterfaceList, + bmtVtable* bmtVT, + bmtMethodInfo* bmtMethod, + bmtTypeInfo* bmtType, + bmtErrorInfo* bmtError, + bmtProperties* bmtProp, + bmtParentInfo* bmtParent) +{ + STANDARD_VM_CONTRACT; + + DWORD i; + BOOL fParentInterface; + + for (WORD wCurInterface = 0; + wCurInterface < bmtInterface->wInterfaceMapSize; + wCurInterface++) + { + fParentInterface = FALSE; + // Keep track of the current interface + InterfaceInfo_t *pCurItfInfo = &(bmtInterface->pInterfaceMap[wCurInterface]); + // The interface we are attempting to place + MethodTable *pInterface = pCurItfInfo->m_pMethodTable; + + _ASSERTE(!(pCurItfInfo->IsDeclaredOnClass() && + !pInterface->IsExternallyVisible() && + pInterface->GetAssembly() != bmtType->pModule->GetAssembly() && + !Security::CanSkipVerification(GetAssembly()->GetDomainAssembly()))); + + // Did we place this interface already due to the parent class's interface placement? + if (pCurItfInfo->GetInteropStartSlot() != MethodTable::NO_SLOT) + { + // If we have declared it then we re-lay it out + if(pCurItfInfo->IsDeclaredOnClass()) + { + // This should be in the outer IF statement, not this inner one, but we'll keep + // it this way to remain consistent for backward compatibility. + fParentInterface = TRUE; + + // If the interface is folded into the non-interface portion of the vtable, we need to unfold it. + WORD wStartSlot = pCurItfInfo->GetInteropStartSlot(); + MethodTable::MethodIterator it(pInterface); + for (; it.IsValid(); it.Next()) + { + if (it.IsVirtual()) + { + if(bmtVT->ppSDVtable[wStartSlot+it.GetSlotNumber()]->wSlot == wStartSlot+it.GetSlotNumber()) + { // If the MD slot is equal to the vtable slot number, then this means the interface + // was folded into the non-interface part of the vtable and needs to get unfolded + // in case a specific override occurs for one of the conceptually two distinct + // slots and not the other (i.e., a MethodImpl overrides an interface method but not + // the class' virtual method). + pCurItfInfo->SetInteropStartSlot(MethodTable::NO_SLOT); + fParentInterface = FALSE; + break; + } + } + } + } + else + { + continue; + } + } + + if (pInterface->GetNumVirtuals() == 0) + { + // no calls can be made to this interface anyway + // so initialize the slot number to 0 + pCurItfInfo->SetInteropStartSlot((WORD) 0); + continue; + } + + // If this interface has not been given a starting position do that now. + if(!fParentInterface) + pCurItfInfo->SetInteropStartSlot(bmtVT->wCurrentVtableSlot); + + // For each method declared in this interface + { + MethodTable::MethodIterator it(pInterface); + for (; it.IsValid(); it.Next()) + { + if (it.IsVirtual()) + { + DWORD dwMemberAttrs; + + // See if we have info gathered while placing members + if (bmtInterface->pppInterfaceImplementingMD[wCurInterface] && bmtInterface->pppInterfaceImplementingMD[wCurInterface][it.GetSlotNumber()] != NULL) + { + bmtInterface->ppInterfaceMethodDescList[it.GetSlotNumber()] = bmtInterface->pppInterfaceImplementingMD[wCurInterface][it.GetSlotNumber()]; + bmtInterface->ppInterfaceDeclMethodDescList[it.GetSlotNumber()] = bmtInterface->pppInterfaceDeclaringMD[wCurInterface][it.GetSlotNumber()]; + continue; + } + + MethodDesc *pInterfaceMD = pInterface->GetMethodDescForSlot(it.GetSlotNumber()); + _ASSERTE(pInterfaceMD != NULL); + + LPCUTF8 pszInterfaceMethodName = pInterfaceMD->GetNameOnNonArrayClass(); + PCCOR_SIGNATURE pInterfaceMethodSig; + DWORD cInterfaceMethodSig; + + pInterfaceMD->GetSig(&pInterfaceMethodSig, &cInterfaceMethodSig); + + // Try to find the method explicitly declared in our class + for (i = 0; i < NumDeclaredMethods(); i++) + { + // look for interface method candidates only + dwMemberAttrs = bmtMethod->rgMethodAttrs[i]; + + // Note that non-publics can legally be exposed via an interface. + if (IsMdVirtual(dwMemberAttrs) && IsMdPublic(dwMemberAttrs)) + { + LPCUTF8 pszMemberName; + + pszMemberName = bmtMethod->rgszMethodName[i]; + _ASSERTE(!(pszMemberName == NULL)); + +#ifdef _DEBUG + if(GetHalfBakedClass()->m_fDebuggingClass && g_pConfig->ShouldBreakOnMethod(pszMemberName)) + CONSISTENCY_CHECK_MSGF(false, ("BreakOnMethodName: '%s' ", pszMemberName)); +#endif // _DEBUG + + if (strcmp(pszMemberName,pszInterfaceMethodName) == 0) + { + PCCOR_SIGNATURE pMemberSignature; + DWORD cMemberSignature; + + _ASSERTE(TypeFromToken(bmtMethod->rgMethodTokens[i]) == mdtMethodDef); + if (FAILED(bmtType->pMDImport->GetSigOfMethodDef( + bmtMethod->rgMethodTokens[i], + &cMemberSignature, + &pMemberSignature))) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); + } + + if (MetaSig::CompareMethodSigs( + pMemberSignature, + cMemberSignature, + bmtType->pModule, NULL, + pInterfaceMethodSig, + cInterfaceMethodSig, + pInterfaceMD->GetModule(), NULL)) + { // Found match, break from loop + break; + } + } + } + } // end ... try to find method + + _ASSERTE(it.GetSlotNumber() < bmtInterface->dwLargestInterfaceSize); + + if (i >= NumDeclaredMethods()) + { + // if this interface has been layed out by our parent then + // we do not need to define a new method desc for it + if(fParentInterface) + { + bmtInterface->ppInterfaceMethodDescList[it.GetSlotNumber()] = NULL; + bmtInterface->ppInterfaceDeclMethodDescList[it.GetSlotNumber()] = NULL; + } + else + { + // We will use the interface implemenation if we do not find one in the + // parent. It will have to be overriden by the a method impl unless the + // class is abstract or it is a special COM type class. + + MethodDesc* pParentMD = NULL; + if(bmtParent->pParentMethodTable) + { +#ifdef _DEBUG + if(GetHalfBakedClass()->m_fDebuggingClass && g_pConfig->ShouldBreakOnMethod(pszInterfaceMethodName)) + CONSISTENCY_CHECK_MSGF(false, ("BreakOnMethodName: '%s' ", pszInterfaceMethodName)); +#endif // _DEBUG + // Check the parent class + pParentMD = MemberLoader::FindMethod(bmtParent->pParentMethodTable, + pszInterfaceMethodName, + pInterfaceMethodSig, + cInterfaceMethodSig, + pInterfaceMD->GetModule(), + MemberLoader::FM_Default, + &bmtParent->parentSubst); + } + // make sure we do a better back patching for these methods + if(pParentMD && IsMdVirtual(pParentMD->GetAttrs())) + { + bmtInterface->ppInterfaceMethodDescList[it.GetSlotNumber()] = pParentMD; + bmtInterface->ppInterfaceDeclMethodDescList[it.GetSlotNumber()] = pInterfaceMD; + } + else + { + bmtInterface->ppInterfaceMethodDescList[it.GetSlotNumber()] = pInterfaceMD; + bmtVT->pInteropData->GetData(pInterfaceMD)->wSlot = pInterfaceMD->GetSlot(); + bmtInterface->ppInterfaceDeclMethodDescList[it.GetSlotNumber()] = NULL; + } + } + } + else + { + // Found as declared method in class. If the interface was layed out by the parent we + // will be overridding their slot so our method counts do not increase. We will fold + // our method into our parent's interface if we have not been placed. + if(fParentInterface) + { + WORD dwSlot = (WORD) (pCurItfInfo->GetInteropStartSlot() + it.GetSlotNumber()); + _ASSERTE(bmtVT->wCurrentVtableSlot > dwSlot); + MethodDesc *pMD = bmtMethod->ppMethodDescList[i]; + InteropMethodTableSlotData *pMDData = bmtVT->pInteropData->GetData(pMD); + _ASSERTE(pMD && "Missing MethodDesc for declared method in class."); + if(pMDData->wSlot == MethodTable::NO_SLOT) + { + pMDData->wSlot = dwSlot; + } + + // Set the slot and interop data + bmtVT->SetMethodDescForSlot(dwSlot, pMD); + bmtVT->ppSDVtable[dwSlot] = pMDData; + _ASSERTE( bmtVT->GetMethodDescForSlot(dwSlot) != NULL); + bmtInterface->ppInterfaceMethodDescList[it.GetSlotNumber()] = NULL; + bmtInterface->ppInterfaceDeclMethodDescList[it.GetSlotNumber()] = NULL; + } + else + { + bmtInterface->ppInterfaceMethodDescList[it.GetSlotNumber()] = bmtMethod->ppMethodDescList[i]; + bmtInterface->ppInterfaceDeclMethodDescList[it.GetSlotNumber()] = pInterfaceMD; + } + } + } + } + } + + { + MethodTable::MethodIterator it(pInterface); + for (; it.IsValid(); it.Next()) + { + if (it.IsVirtual()) + { + // The entry can be null if the interface was previously + // laid out by a parent and we did not have a method + // that subclassed the interface. + if(bmtInterface->ppInterfaceMethodDescList[it.GetSlotNumber()] != NULL) + { + // Get the MethodDesc which was allocated for the method + MethodDesc *pMD = bmtInterface->ppInterfaceMethodDescList[it.GetSlotNumber()]; + InteropMethodTableSlotData *pMDData = bmtVT->pInteropData->GetData(pMD); + + if (pMDData->wSlot == (WORD) MethodTable::NO_SLOT) + { + pMDData->wSlot = (WORD) bmtVT->wCurrentVtableSlot; + } + + // Set the vtable slot + _ASSERTE(bmtVT->GetMethodDescForSlot(bmtVT->wCurrentVtableSlot) == NULL); + bmtVT->SetMethodDescForSlot(bmtVT->wCurrentVtableSlot, pMD); + _ASSERTE(bmtVT->GetMethodDescForSlot(bmtVT->wCurrentVtableSlot) != NULL); + bmtVT->ppSDVtable[bmtVT->wCurrentVtableSlot] = pMDData; + + // Increment the current vtable slot + bmtVT->wCurrentVtableSlot++; + } + } + } + } + } +} + +//--------------------------------------------------------------------------------------- +// We should have collected all the method impls. Cycle through them creating the method impl +// structure that holds the information about which slots are overridden. +VOID MethodTableBuilder::BuildInteropVTable_PlaceMethodImpls( + BaseDomain *bmtDomain, + bmtTypeInfo* bmtType, + bmtMethodImplInfo* bmtMethodImpl, + bmtErrorInfo* bmtError, + bmtInterfaceInfo* bmtInterface, + bmtVtable* bmtVT, + bmtParentInfo* bmtParent) + +{ + STANDARD_VM_CONTRACT; + + if(bmtMethodImpl->pIndex == 0) + return; + + DWORD pIndex = 0; + + // Allocate some temporary storage. The number of overrides for a single method impl + // cannot be greater then the number of vtable slots. + DWORD* slots = (DWORD*) new (&GetThread()->m_MarshalAlloc) DWORD[bmtVT->wCurrentVtableSlot]; + MethodDesc **replaced = new (&GetThread()->m_MarshalAlloc) MethodDesc*[bmtVT->wCurrentVtableSlot]; + + while(pIndex < bmtMethodImpl->pIndex) { + + DWORD slotIndex = 0; + DWORD dwItfCount = 0; + MethodDesc* next = bmtMethodImpl->GetBodyMethodDesc(pIndex); + MethodDesc* body = NULL; + + // The signature for the body of the method impl. We cache the signature until all + // the method impl's using the same body are done. + PCCOR_SIGNATURE pBodySignature = NULL; + DWORD cBodySignature = 0; + + // The impls are sorted according to the method descs for the body of the method impl. + // Loop through the impls until the next body is found. When a single body + // has been done move the slots implemented and method descs replaced into the storage + // found on the body method desc. + do { // collect information until we reach the next body + body = next; + + // Get the declaration part of the method impl. It will either be a token + // (declaration is on this type) or a method desc. + MethodDesc* pDecl = bmtMethodImpl->GetDeclarationMethodDesc(pIndex); + if(pDecl == NULL) { + // The declaration is on this type to get the token. + mdMethodDef mdef = bmtMethodImpl->GetDeclarationToken(pIndex); + + BuildInteropVTable_PlaceLocalDeclaration(mdef, + body, + bmtType, + bmtError, + bmtVT, + slots, // Adds override to the slot and replaced arrays. + replaced, + &slotIndex, // Increments count + &pBodySignature, // Fills in the signature + &cBodySignature); + } + else { + // Method impls to methods on generic interfaces should have already + // been filtered out. + _ASSERTE(pDecl->GetMethodTable()->GetNumGenericArgs() == 0); + + if(pDecl->GetMethodTable()->IsInterface()) { + BuildInteropVTable_PlaceInterfaceDeclaration(pDecl, + body, + bmtMethodImpl->GetDeclarationSubst(pIndex), + bmtType, + bmtInterface, + bmtError, + bmtVT, + slots, + replaced, + &slotIndex, // Increments count + &pBodySignature, // Fills in the signature + &cBodySignature); + } + else { + BuildInteropVTable_PlaceParentDeclaration(pDecl, + body, + bmtMethodImpl->GetDeclarationSubst(pIndex), + bmtType, + bmtError, + bmtVT, + bmtParent, + slots, + replaced, + &slotIndex, // Increments count + &pBodySignature, // Fills in the signature + &cBodySignature); + } + } + + // Move to the next body + pIndex++; + + // we hit the end of the list so leave + next = pIndex < bmtMethodImpl->pIndex ? bmtMethodImpl->GetBodyMethodDesc(pIndex) : NULL; + } while(next == body) ; + } // while(next != NULL) +} + +//--------------------------------------------------------------------------------------- +VOID MethodTableBuilder::BuildInteropVTable_PlaceLocalDeclaration( + mdMethodDef mdef, + MethodDesc* body, + bmtTypeInfo* bmtType, + bmtErrorInfo* bmtError, + bmtVtable* bmtVT, + DWORD* slots, + MethodDesc** replaced, + DWORD* pSlotIndex, + PCCOR_SIGNATURE* ppBodySignature, + DWORD* pcBodySignature) +{ + STANDARD_VM_CONTRACT; + + // we search on the token and m_cl + for(USHORT i = 0; i < bmtVT->wCurrentVtableSlot; i++) + { + // Make sure we haven't already been MethodImpl'd + _ASSERTE(bmtVT->ppSDVtable[i]->pMD == bmtVT->ppSDVtable[i]->pDeclMD); + + // We get the current slot. Since we are looking for a method declaration + // that is on our class we would never match up with a method obtained from + // one of our parents or an Interface. + MethodDesc *pMD = bmtVT->ppSDVtable[i]->pMD; + + // If we get a null then we have already replaced this one. We can't check it + // so we will just by by-pass this. + if(pMD->GetMemberDef() == mdef) + { + InteropMethodTableSlotData *pDeclData = bmtVT->pInteropData->GetData(pMD); + InteropMethodTableSlotData *pImplData = bmtVT->pInteropData->GetData(body); + + // If the body has not been placed then place it here. We do not + // place bodies for method impl's until we find a spot for them. + if (pImplData->wSlot == MethodTable::NO_SLOT) + { + pImplData->wSlot = (WORD) i; + } + + // We implement this slot, record it + slots[*pSlotIndex] = i; + replaced[*pSlotIndex] = pMD; + bmtVT->SetMethodDescForSlot(i, body); + pDeclData->pMD = pImplData->pMD; + pDeclData->wSlot = pImplData->wSlot; + bmtVT->ppSDVtable[i] = pDeclData; + + // increment the counter + (*pSlotIndex)++; + } + } +} + +//--------------------------------------------------------------------------------------- +VOID MethodTableBuilder::BuildInteropVTable_PlaceInterfaceDeclaration( + MethodDesc* pItfDecl, + MethodDesc* pImplBody, + const Substitution *pDeclSubst, + bmtTypeInfo* bmtType, + bmtInterfaceInfo* bmtInterface, + bmtErrorInfo* bmtError, + bmtVtable* bmtVT, + DWORD* slots, + MethodDesc** replaced, + DWORD* pSlotIndex, + PCCOR_SIGNATURE* ppBodySignature, + DWORD* pcBodySignature) +{ + STANDARD_VM_CONTRACT; + + _ASSERTE(pItfDecl && pItfDecl->IsInterface() && !(pItfDecl->IsMethodImpl())); + + // the fact that an interface only shows up once in the vtable + // When we are looking for a method desc then the declaration is on + // some class or interface that this class implements. The declaration + // will either be to an interface or to a class. If it is to a + // interface then we need to search for that interface. From that + // slot number of the method in the interface we can calculate the offset + // into our vtable. If it is to a class it must be a subclass. This uses + // the fact that an interface only shows up once in the vtable. + + BOOL fInterfaceFound = FALSE; + // Check our vtable for entries that we are suppose to override. + // Since this is an external method we must also check the inteface map. + // We want to replace any interface methods even if they have been replaced + // by a base class. + for(USHORT i = 0; i < bmtInterface->wInterfaceMapSize; i++) + { + MethodTable *pInterface = bmtInterface->pInterfaceMap[i].m_pMethodTable; + + if (pInterface->IsEquivalentTo(pItfDecl->GetMethodTable())) + { + // We found an interface so no error + fInterfaceFound = TRUE; + + WORD wSlot = (WORD) -1; + MethodDesc *pMD = NULL; + + // Find out where the interface map is set on our vtable + WORD wStartingSlot = (USHORT) bmtInterface->pInterfaceMap[i].GetInteropStartSlot(); + + // We need to duplicate the interface to avoid copies. Currently, interfaces + // do not overlap so we just need to check to see if there is a non-duplicated + // MD. If there is then the interface shares it with the class which means + // we need to copy the whole interface + for(wSlot = wStartingSlot; wSlot < pInterface->GetNumVirtuals() + wStartingSlot; wSlot++) + { + // This check will tell us if the method in this slot is the first instance (not a duplicate) + if(bmtVT->ppSDVtable[wSlot]->wSlot == wSlot) + break; + } + + if(wSlot < pInterface->GetNumVirtuals() + wStartingSlot) + { + // Check to see if we have allocated the temporay array of starting values. + // This array is used to backpatch entries to the original location. These + // values are never used but will cause problems later when we finish + // laying out the method table. + if(bmtInterface->pdwOriginalStart == NULL) + { + Thread *pThread = GetThread(); + bmtInterface->pdwOriginalStart = new (&pThread->m_MarshalAlloc) DWORD[bmtInterface->dwMaxExpandedInterfaces]; + memset(bmtInterface->pdwOriginalStart, 0, sizeof(DWORD)*bmtInterface->dwMaxExpandedInterfaces); + } + + _ASSERTE(bmtInterface->pInterfaceMap[i].GetInteropStartSlot() != (WORD) 0 && "We assume that an interface does not start at position 0"); + _ASSERTE(bmtInterface->pdwOriginalStart[i] == 0 && "We should not move an interface twice"); + bmtInterface->pdwOriginalStart[i] = bmtInterface->pInterfaceMap[i].GetInteropStartSlot(); + + // The interface now starts at the end of the map. + bmtInterface->pInterfaceMap[i].SetInteropStartSlot(bmtVT->wCurrentVtableSlot); + for(WORD d = wStartingSlot; d < pInterface->GetNumVirtuals() + wStartingSlot; d++) + { + // Copy the MD + //@TODO: Maybe need to create new slot data entries for this copy-out based on + //@TODO: the MD's of the interface slots. + InteropMethodTableSlotData *pDataCopy = bmtVT->ppSDVtable[d]; + bmtVT->SetMethodDescForSlot(bmtVT->wCurrentVtableSlot, pDataCopy->pMD); + bmtVT->ppSDVtable[bmtVT->wCurrentVtableSlot] = pDataCopy; + // Increment the various counters + bmtVT->wCurrentVtableSlot++; + } + // Reset the starting slot to the known value + wStartingSlot = bmtInterface->pInterfaceMap[i].GetInteropStartSlot(); + } + + // Make sure we have placed the interface map. + _ASSERTE(wStartingSlot != MethodTable::NO_SLOT); + + // Get the Slot location of the method desc (slot of the itf MD + start slot for this class) + wSlot = pItfDecl->GetSlot() + wStartingSlot; + _ASSERTE(wSlot < bmtVT->wCurrentVtableSlot); + + // Get our current method desc for this slot + pMD = bmtVT->ppSDVtable[wSlot]->pMD; + + // If we have not got the method impl signature go get it now. It is cached + // in our caller + if (*ppBodySignature == NULL) + { + if (FAILED(bmtType->pMDImport->GetSigOfMethodDef( + pImplBody->GetMemberDef(), + pcBodySignature, + ppBodySignature))) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); + } + } + + InteropMethodTableSlotData *pImplSlotData = bmtVT->pInteropData->GetData(pImplBody); + _ASSERTE(pImplSlotData->wSlot != MethodTable::NO_SLOT); + // If the body has not been placed then place it now. + if (pImplSlotData->wSlot == MethodTable::NO_SLOT) + { + pImplSlotData->wSlot = wSlot; + } + + // Store away the values + InteropMethodTableSlotData *pItfSlotData = bmtVT->pInteropData->GetData(pItfDecl); + slots[*pSlotIndex] = wSlot; + replaced[*pSlotIndex] = pItfDecl; + bmtVT->SetMethodDescForSlot(wSlot, pImplBody); + pItfSlotData->pMD = pImplBody; + pItfSlotData->wSlot = pImplSlotData->wSlot; + bmtVT->ppSDVtable[wSlot] = pItfSlotData; + + // increment the counter + (*pSlotIndex)++; + + // if we have moved the interface we need to back patch the original location + // if we had left an interface place holder. + if(bmtInterface->pdwOriginalStart && bmtInterface->pdwOriginalStart[i] != 0) + { + USHORT slot = (USHORT) bmtInterface->pdwOriginalStart[i] + pItfDecl->GetSlot(); + MethodDesc* pSlotMD = bmtVT->ppSDVtable[slot]->pMD; + if(pSlotMD->GetMethodTable() && pSlotMD->IsInterface()) + { + bmtVT->SetMethodDescForSlot(slot, pImplBody); + bmtVT->ppSDVtable[slot] = pItfSlotData; + } + } + break; + } + } + + _ASSERTE(fInterfaceFound); +} + +//--------------------------------------------------------------------------------------- +VOID MethodTableBuilder::BuildInteropVTable_PlaceParentDeclaration( + MethodDesc* pDecl, + MethodDesc* pImplBody, + const Substitution *pDeclSubst, + bmtTypeInfo* bmtType, + bmtErrorInfo* bmtError, + bmtVtable* bmtVT, + bmtParentInfo* bmtParent, + DWORD* slots, + MethodDesc** replaced, + DWORD* pSlotIndex, + PCCOR_SIGNATURE* ppBodySignature, + DWORD* pcBodySignature) +{ + STANDARD_VM_CONTRACT; + + _ASSERTE(pDecl && !pDecl->IsInterface()); + + BOOL fRet = FALSE; + + // Verify that the class of the declaration is in our heirarchy + MethodTable* declType = pDecl->GetMethodTable(); + MethodTable* pParentMT = bmtParent->pParentMethodTable; + while(pParentMT != NULL) + { + + if(declType == pParentMT) + break; + pParentMT = pParentMT->GetParentMethodTable(); + } + _ASSERTE(pParentMT); + + // Compare the signature for the token in the specified scope + // If we have not got the method impl signature go get it now + if (*ppBodySignature == NULL) + { + if (FAILED(bmtType->pMDImport->GetSigOfMethodDef( + pImplBody->GetMemberDef(), + pcBodySignature, + ppBodySignature))) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); + } + } + + // We get the method from the parents slot. We will replace the method that is currently + // defined in that slot and any duplicates for that method desc. + WORD wSlot = InteropMethodTableData::GetSlotForMethodDesc(pParentMT, pDecl); + InteropMethodTableSlotData *pDeclData = bmtVT->ppSDVtable[wSlot]; + InteropMethodTableSlotData *pImplData = bmtVT->pInteropData->GetData(pImplBody); + + // Get the real method desc (a base class may have overridden the method + // with a method impl) + MethodDesc* pReplaceDesc = pDeclData->pDeclMD; + + // If the body has not been placed then place it here + if(pImplData->wSlot == MethodTable::NO_SLOT) + { + pImplData->wSlot = wSlot; + } + + slots[*pSlotIndex] = wSlot; + replaced[*pSlotIndex] = pReplaceDesc; + bmtVT->SetMethodDescForSlot(wSlot, pImplBody); + pDeclData->pMD = pImplData->pMD; + pDeclData->wSlot = pImplData->wSlot; + bmtVT->ppSDVtable[wSlot] = pDeclData; + + // increment the counter + (*pSlotIndex)++; + + // we search for all duplicates + for(USHORT i = wSlot+1; i < bmtVT->wCurrentVtableSlot; i++) + { + MethodDesc *pMD = bmtVT->ppSDVtable[i]->pMD; + + MethodDesc* pRealDesc = bmtVT->ppSDVtable[i]->pDeclMD; + + if(pRealDesc == pReplaceDesc) + { + // We do not want to override a body to another method impl + _ASSERTE(!pRealDesc->IsMethodImpl()); + + // Make sure we are not overridding another method impl + _ASSERTE(!(pMD != pImplBody && pMD->IsMethodImpl() && pMD->GetMethodTable() == NULL)); + + slots[*pSlotIndex] = i; + replaced[*pSlotIndex] = pRealDesc; + bmtVT->pVtable[i] = bmtVT->pVtable[wSlot]; + bmtVT->pVtableMD[i] = bmtVT->pVtableMD[wSlot]; + bmtVT->ppSDVtable[i] = bmtVT->ppSDVtable[wSlot]; + + // increment the counter + (*pSlotIndex)++; + } + } +} + +//--------------------------------------------------------------------------------------- +VOID MethodTableBuilder::BuildInteropVTable_PropagateInheritance( + bmtVtable *bmtVT) +{ + STANDARD_VM_CONTRACT; + + for (DWORD i = 0; i < bmtVT->wCurrentVtableSlot; i++) + { + // For now only propagate inheritance for method desc that are not interface MD's. + // This is not sufficient but InterfaceImpl's will complete the picture. + InteropMethodTableSlotData *pMDData = bmtVT->ppSDVtable[i]; + MethodDesc* pMD = pMDData->pMD; + CONSISTENCY_CHECK_MSG(CheckPointer(pMD), "Could not resolve MethodDesc Slot!"); + + if(!pMD->IsInterface() && pMDData->GetSlot() != i) + { + pMDData->SetDuplicate(); + bmtVT->pVtable[i] = bmtVT->pVtable[pMDData->GetSlot()]; + bmtVT->pVtableMD[i] = bmtVT->pVtableMD[pMDData->GetSlot()]; + bmtVT->ppSDVtable[i]->pMD = bmtVT->ppSDVtable[pMDData->GetSlot()]->pMD; + } + } +} + + +//--------------------------------------------------------------------------------------- +VOID MethodTableBuilder::FinalizeInteropVTable( + AllocMemTracker *pamTracker, + LoaderAllocator* pAllocator, + bmtVtable* bmtVT, + bmtInterfaceInfo* bmtInterface, + bmtTypeInfo* bmtType, + bmtProperties* bmtProp, + bmtMethodInfo* bmtMethod, + bmtErrorInfo* bmtError, + bmtParentInfo* bmtParent, + InteropMethodTableData **ppInteropMT) +{ + STANDARD_VM_CONTRACT; + + LoaderHeap *pHeap = pAllocator->GetLowFrequencyHeap(); + + // Allocate the overall structure + InteropMethodTableData *pMTData = (InteropMethodTableData *) pamTracker->Track(pHeap->AllocMem(S_SIZE_T(sizeof(InteropMethodTableData)))); +#ifdef LOGGING + g_sdStats.m_cbComInteropData += sizeof(InteropMethodTableData); +#endif + memset(pMTData, 0, sizeof(InteropMethodTableData)); + + // Allocate the vtable + pMTData->cVTable = bmtVT->wCurrentVtableSlot; + if (pMTData->cVTable != 0) + { + pMTData->pVTable = (InteropMethodTableSlotData *) + pamTracker->Track(pHeap->AllocMem(S_SIZE_T(sizeof(InteropMethodTableSlotData)) * S_SIZE_T(pMTData->cVTable))); +#ifdef LOGGING + g_sdStats.m_cbComInteropData += sizeof(InteropMethodTableSlotData) * pMTData->cVTable; +#endif + + { // Copy the vtable + for (DWORD i = 0; i < pMTData->cVTable; i++) + { + CONSISTENCY_CHECK(bmtVT->ppSDVtable[i]->wSlot != MethodTable::NO_SLOT); + pMTData->pVTable[i] = *bmtVT->ppSDVtable[i]; + } + } + } + + // Allocate the non-vtable + pMTData->cNonVTable = bmtVT->wCurrentNonVtableSlot; + if (pMTData->cNonVTable != 0) + { + pMTData->pNonVTable = (InteropMethodTableSlotData *) + pamTracker->Track(pHeap->AllocMem(S_SIZE_T(sizeof(InteropMethodTableSlotData)) * S_SIZE_T(pMTData->cNonVTable))); +#ifdef LOGGING + g_sdStats.m_cbComInteropData += sizeof(InteropMethodTableSlotData) * pMTData->cNonVTable; +#endif + + { // Copy the non-vtable + for (DWORD i = 0; i < pMTData->cNonVTable; i++) + { + CONSISTENCY_CHECK(bmtVT->ppSDVtable[i]->wSlot != MethodTable::NO_SLOT); + pMTData->pNonVTable[i] = *bmtVT->ppSDNonVtable[i]; + } + } + } + + // Allocate the interface map + pMTData->cInterfaceMap = bmtInterface->wInterfaceMapSize; + if (pMTData->cInterfaceMap != 0) + { + pMTData->pInterfaceMap = (InterfaceInfo_t *) + pamTracker->Track(pHeap->AllocMem(S_SIZE_T(sizeof(InterfaceInfo_t)) * S_SIZE_T(pMTData->cInterfaceMap))); +#ifdef LOGGING + g_sdStats.m_cbComInteropData += sizeof(InterfaceInfo_t) * pMTData->cInterfaceMap; +#endif + + { // Copy the interface map + for (DWORD i = 0; i < pMTData->cInterfaceMap; i++) + { + pMTData->pInterfaceMap[i] = bmtInterface->pInterfaceMap[i]; + } + } + } + + *ppInteropMT = pMTData; +} + +//******************************************************************************* +VOID MethodTableBuilder::EnumerateMethodImpls() +{ + STANDARD_VM_CONTRACT; + + HRESULT hr = S_OK; + IMDInternalImport *pMDInternalImport = bmtType->pMDImport; + DWORD rid, maxRidMD, maxRidMR; + hr = bmtMethodImpl->hEnumMethodImpl.EnumMethodImplInitNoThrow(GetCl()); + + if (FAILED(hr)) + { + BuildMethodTableThrowException(hr, *bmtError); + } + + // This gets the count out of the metadata interface. + bmtMethodImpl->dwNumberMethodImpls = bmtMethodImpl->hEnumMethodImpl.EnumMethodImplGetCount(); + + // This is the first pass. In this we will simply enumerate the token pairs and fill in + // the data structures. In addition, we'll sort the list and eliminate duplicates. + if (bmtMethodImpl->dwNumberMethodImpls > 0) + { + // + // Allocate the structures to keep track of the token pairs + // + bmtMethodImpl->rgMethodImplTokens = new (&GetThread()->m_MarshalAlloc) + bmtMethodImplInfo::MethodImplTokenPair[bmtMethodImpl->dwNumberMethodImpls]; + + // Iterate through each MethodImpl declared on this class + for (DWORD i = 0; i < bmtMethodImpl->dwNumberMethodImpls; i++) + { + // Grab the next set of body/decl tokens + hr = bmtMethodImpl->hEnumMethodImpl.EnumMethodImplNext( + &bmtMethodImpl->rgMethodImplTokens[i].methodBody, + &bmtMethodImpl->rgMethodImplTokens[i].methodDecl); + if (FAILED(hr)) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); + } + if (hr == S_FALSE) + { + // In the odd case that the enumerator fails before we've reached the total reported + // entries, let's reset the count and just break out. (Should we throw?) + bmtMethodImpl->dwNumberMethodImpls = i; + break; + } + } + + // No need to do any sorting or duplicate elimination if there's not two or more methodImpls + if (bmtMethodImpl->dwNumberMethodImpls > 1) + { + // Now sort + qsort(bmtMethodImpl->rgMethodImplTokens, + bmtMethodImpl->dwNumberMethodImpls, + sizeof(bmtMethodImplInfo::MethodImplTokenPair), + &bmtMethodImplInfo::MethodImplTokenPair::Compare); + + // Now eliminate duplicates + for (DWORD i = 0; i < bmtMethodImpl->dwNumberMethodImpls - 1; i++) + { + CONSISTENCY_CHECK((i + 1) < bmtMethodImpl->dwNumberMethodImpls); + + bmtMethodImplInfo::MethodImplTokenPair *e1 = &bmtMethodImpl->rgMethodImplTokens[i]; + bmtMethodImplInfo::MethodImplTokenPair *e2 = &bmtMethodImpl->rgMethodImplTokens[i + 1]; + + // If the pair are equal, eliminate the first one, and reduce the total count by one. + if (bmtMethodImplInfo::MethodImplTokenPair::Equal(e1, e2)) + { + DWORD dwCopyNum = bmtMethodImpl->dwNumberMethodImpls - (i + 1); + memcpy(e1, e2, dwCopyNum * sizeof(bmtMethodImplInfo::MethodImplTokenPair)); + bmtMethodImpl->dwNumberMethodImpls--; + CONSISTENCY_CHECK(bmtMethodImpl->dwNumberMethodImpls > 0); + } + } + } + } + + if (bmtMethodImpl->dwNumberMethodImpls != 0) + { + // + // Allocate the structures to keep track of the impl matches + // + bmtMethodImpl->pMethodDeclSubsts = new (&GetThread()->m_MarshalAlloc) Substitution[bmtMethodImpl->dwNumberMethodImpls]; + bmtMethodImpl->rgEntries = new (&GetThread()->m_MarshalAlloc) bmtMethodImplInfo::Entry[bmtMethodImpl->dwNumberMethodImpls]; + + // These are used for verification + maxRidMD = pMDInternalImport->GetCountWithTokenKind(mdtMethodDef); + maxRidMR = pMDInternalImport->GetCountWithTokenKind(mdtMemberRef); + + // Iterate through each MethodImpl declared on this class + for (DWORD i = 0; i < bmtMethodImpl->dwNumberMethodImpls; i++) + { + PCCOR_SIGNATURE pSigDecl = NULL; + PCCOR_SIGNATURE pSigBody = NULL; + ULONG cbSigDecl; + ULONG cbSigBody; + mdToken tkParent; + + mdToken theBody, theDecl; + Substitution theDeclSubst(bmtType->pModule, SigPointer(), NULL); // this can get updated later below. + + theBody = bmtMethodImpl->rgMethodImplTokens[i].methodBody; + theDecl = bmtMethodImpl->rgMethodImplTokens[i].methodDecl; + + // IMPLEMENTATION LIMITATION: currently, we require that the body of a methodImpl + // belong to the current type. This is because we need to allocate a different + // type of MethodDesc for bodies that are part of methodImpls. + if (TypeFromToken(theBody) != mdtMethodDef) + { + mdToken theNewBody; + hr = FindMethodDeclarationForMethodImpl(bmtType->pMDImport, + GetCl(), + theBody, + &theNewBody); + if (FAILED(hr)) + { + BuildMethodTableThrowException(hr, IDS_CLASSLOAD_MI_ILLEGAL_BODY, mdMethodDefNil); + } + theBody = theNewBody; + + // Make sure to update the stored token with the resolved token. + bmtMethodImpl->rgMethodImplTokens[i].methodBody = theBody; + } + + if (TypeFromToken(theBody) != mdtMethodDef) + { + BuildMethodTableThrowException(BFA_METHODDECL_NOT_A_METHODDEF); + } + CONSISTENCY_CHECK(theBody == bmtMethodImpl->rgMethodImplTokens[i].methodBody); + + // + // Now that the tokens of Decl and Body are obtained, do the MD validation + // + + rid = RidFromToken(theDecl); + + // Perform initial rudimentary validation of the token. Full token verification + // will be done in TestMethodImpl when placing the methodImpls. + if (TypeFromToken(theDecl) == mdtMethodDef) + { + // Decl must be valid token + if ((rid == 0)||(rid > maxRidMD)) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_MI_ILLEGAL_TOKEN_DECL); + } + // Get signature and length + if (FAILED(pMDInternalImport->GetSigOfMethodDef(theDecl, &cbSigDecl, &pSigDecl))) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); + } + } + + // The token is not a MethodDef (likely a MemberRef) + else + { + // Decl must be valid token + if ((TypeFromToken(theDecl) != mdtMemberRef) || (rid == 0) || (rid > maxRidMR)) + { + bmtError->resIDWhy = IDS_CLASSLOAD_MI_ILLEGAL_TOKEN_DECL; + BuildMethodTableThrowException(IDS_CLASSLOAD_MI_ILLEGAL_TOKEN_DECL); + } + + // Get signature and length + LPCSTR szDeclName; + if (FAILED(pMDInternalImport->GetNameAndSigOfMemberRef(theDecl, &pSigDecl, &cbSigDecl, &szDeclName))) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); + } + + // Get parent + hr = pMDInternalImport->GetParentToken(theDecl,&tkParent); + if (FAILED(hr)) + BuildMethodTableThrowException(hr, *bmtError); + + theDeclSubst = Substitution(tkParent, bmtType->pModule, NULL); + } + + // Perform initial rudimentary validation of the token. Full token verification + // will be done in TestMethodImpl when placing the methodImpls. + { + // Body must be valid token + rid = RidFromToken(theBody); + if ((rid == 0)||(rid > maxRidMD)) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_MI_ILLEGAL_TOKEN_BODY); + } + // Body's parent must be this class + hr = pMDInternalImport->GetParentToken(theBody,&tkParent); + if (FAILED(hr)) + BuildMethodTableThrowException(hr, *bmtError); + if(tkParent != GetCl()) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_MI_ILLEGAL_BODY); + } + } + // Decl's and Body's signatures must match + if ((pSigDecl != NULL) && (cbSigDecl != 0)) + { + if (FAILED(pMDInternalImport->GetSigOfMethodDef(theBody,&cbSigBody, &pSigBody)) || + (pSigBody == NULL) || + (cbSigBody == 0)) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MISSING_SIG_BODY); + } + // Can't use memcmp because there may be two AssemblyRefs + // in this scope, pointing to the same assembly, etc.). + if (!MetaSig::CompareMethodSigs(pSigDecl, + cbSigDecl, + bmtType->pModule, + &theDeclSubst, + pSigBody, + cbSigBody, + bmtType->pModule, + NULL)) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_MI_BODY_DECL_MISMATCH); + } + } + else + { + BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MISSING_SIG_DECL); + } + + bmtMethodImpl->pMethodDeclSubsts[i] = theDeclSubst; + + } + } +} + +//******************************************************************************* +// +// Used by BuildMethodTable +// +// Enumerate this class's members +// +#ifdef _PREFAST_ +#pragma warning(push) +#pragma warning(disable:21000) // Suppress PREFast warning about overly large function +#endif +VOID MethodTableBuilder::EnumerateClassMethods() +{ + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(bmtType)); + PRECONDITION(CheckPointer(bmtMethod)); + PRECONDITION(CheckPointer(bmtProp)); + PRECONDITION(CheckPointer(bmtVT)); + PRECONDITION(CheckPointer(bmtError)); + } + CONTRACTL_END; + + HRESULT hr = S_OK; + DWORD i; + Thread *pThread = GetThread(); + IMDInternalImport *pMDInternalImport = bmtType->pMDImport; + mdToken tok; + DWORD dwMemberAttrs; + BOOL fIsClassEnum = IsEnum(); + BOOL fIsClassInterface = IsInterface(); + BOOL fIsClassValueType = IsValueClass(); +#ifdef FEATURE_COMINTEROP + BOOL fIsClassComImport = IsComImport(); +#endif + BOOL fIsClassNotAbstract = (IsTdAbstract(GetAttrClass()) == 0); + PCCOR_SIGNATURE pMemberSignature; + ULONG cMemberSignature; + + // + // Run through the method list and calculate the following: + // # methods. + // # "other" methods (i.e. static or private) + // # non-other methods + // + + bmtVT->dwMaxVtableSize = 0; // we'll fix this later to be the real upper bound on vtable size + bmtMethod->cMethods = 0; + + hr = bmtMethod->hEnumMethod.EnumInitNoThrow(mdtMethodDef, GetCl()); + if (FAILED(hr)) + { + _ASSERTE(!"Cannot count memberdefs"); + if (FAILED(hr)) + { + BuildMethodTableThrowException(hr, *bmtError); + } + } + + // Allocate an array to contain the method tokens as well as information about the methods. + bmtMethod->cMethAndGaps = bmtMethod->hEnumMethod.EnumGetCount(); + + bmtMethod->rgMethodTokens = new (&pThread->m_MarshalAlloc) mdToken[bmtMethod->cMethAndGaps]; + bmtMethod->rgMethodRVA = new (&pThread->m_MarshalAlloc) ULONG[bmtMethod->cMethAndGaps]; + bmtMethod->rgMethodAttrs = new (&pThread->m_MarshalAlloc) DWORD[bmtMethod->cMethAndGaps]; + bmtMethod->rgMethodImplFlags = new (&pThread->m_MarshalAlloc) DWORD[bmtMethod->cMethAndGaps]; + bmtMethod->rgMethodClassifications = new (&pThread->m_MarshalAlloc) DWORD[bmtMethod->cMethAndGaps]; + + bmtMethod->rgszMethodName = new (&pThread->m_MarshalAlloc) LPCSTR[bmtMethod->cMethAndGaps]; + + bmtMethod->rgMethodImpl = new (&pThread->m_MarshalAlloc) BYTE[bmtMethod->cMethAndGaps]; + bmtMethod->rgMethodType = new (&pThread->m_MarshalAlloc) BYTE[bmtMethod->cMethAndGaps]; + + enum { SeenCtor = 1, SeenInvoke = 2, SeenBeginInvoke = 4, SeenEndInvoke = 8}; + unsigned delegateMethodsSeen = 0; + + for (i = 0; i < bmtMethod->cMethAndGaps; i++) + { + ULONG dwMethodRVA; + DWORD dwImplFlags; + DWORD Classification; + LPSTR strMethodName; + + // + // Go to the next method and retrieve its attributes. + // + + bmtMethod->hEnumMethod.EnumNext(&tok); + DWORD rid = RidFromToken(tok); + if ((rid == 0)||(rid > pMDInternalImport->GetCountWithTokenKind(mdtMethodDef))) + { + BuildMethodTableThrowException(BFA_METHOD_TOKEN_OUT_OF_RANGE); + } + + if (FAILED(pMDInternalImport->GetMethodDefProps(tok, &dwMemberAttrs))) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); + } + if (IsMdRTSpecialName(dwMemberAttrs) || IsMdVirtual(dwMemberAttrs) || IsDelegate()) + { + if (FAILED(pMDInternalImport->GetNameOfMethodDef(tok, (LPCSTR *)&strMethodName))) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); + } + if (IsStrLongerThan(strMethodName,MAX_CLASS_NAME)) + { + BuildMethodTableThrowException(BFA_METHOD_NAME_TOO_LONG); + } + } + else + strMethodName = NULL; + + HENUMInternalHolder hEnumTyPars(pMDInternalImport); + hr = hEnumTyPars.EnumInitNoThrow(mdtGenericParam, tok); + if (FAILED(hr)) + { + BuildMethodTableThrowException(hr, *bmtError); + } + + WORD numGenericMethodArgs = (WORD) hEnumTyPars.EnumGetCount(); + + // We do not want to support context-bound objects with generic methods. + if (IsContextful() && numGenericMethodArgs > 0) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_CONTEXT_BOUND_GENERIC_METHOD); + } + + if (numGenericMethodArgs != 0) + { + for (unsigned methIdx = 0; methIdx < numGenericMethodArgs; methIdx++) + { + mdGenericParam tkTyPar; + pMDInternalImport->EnumNext(&hEnumTyPars, &tkTyPar); + DWORD flags; + if (FAILED(pMDInternalImport->GetGenericParamProps(tkTyPar, NULL, &flags, NULL, NULL, NULL))) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); + } + + if (0 != (flags & ~(gpVarianceMask | gpSpecialConstraintMask))) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); + } + switch (flags & gpVarianceMask) + { + case gpNonVariant: + break; + + case gpCovariant: // intentional fallthru + case gpContravariant: + BuildMethodTableThrowException(VLDTR_E_GP_ILLEGAL_VARIANT_MVAR); + break; + + default: + BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); + + } + } + } + + // + // We need to check if there are any gaps in the vtable. These are + // represented by methods with the mdSpecial flag and a name of the form + // _VTblGap_nnn (to represent nnn empty slots) or _VTblGap (to represent a + // single empty slot). + // + + if (IsMdRTSpecialName(dwMemberAttrs)) + { + PREFIX_ASSUME(strMethodName != NULL); // if we've gotten here we've called GetNameOfMethodDef + + // The slot is special, but it might not be a vtable spacer. To + // determine that we must look at the name. + if (strncmp(strMethodName, "_VtblGap", 8) == 0) + { + // + // This slot doesn't really exist, don't add it to the method + // table. Instead it represents one or more empty slots, encoded + // in the method name. Locate the beginning of the count in the + // name. There are these points to consider: + // There may be no count present at all (in which case the + // count is taken as one). + // There may be an additional count just after Gap but before + // the '_'. We ignore this. + // + + LPCSTR pos = strMethodName + 8; + + // Skip optional number. + while (IS_DIGIT(*pos)) + pos++; + + WORD n = 0; + + // Check for presence of count. + if (*pos == '\0') + n = 1; + else + { + if (*pos != '_') + { + BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, + IDS_CLASSLOAD_BADSPECIALMETHOD, + tok); + } + + // Skip '_'. + pos++; + + // Read count. + bool fReadAtLeastOneDigit = false; + while (IS_DIGIT(*pos)) + { + _ASSERTE(n < 6552); + n *= 10; + n += DIGIT_TO_INT(*pos); + pos++; + fReadAtLeastOneDigit = true; + } + + // Check for end of name. + if (*pos != '\0' || !fReadAtLeastOneDigit) + { + BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, + IDS_CLASSLOAD_BADSPECIALMETHOD, + tok); + } + } + +#ifdef FEATURE_COMINTEROP + // Record vtable gap in mapping list. + if (GetHalfBakedClass()->GetSparseCOMInteropVTableMap() == NULL) + GetHalfBakedClass()->SetSparseCOMInteropVTableMap(new SparseVTableMap()); + + GetHalfBakedClass()->GetSparseCOMInteropVTableMap()->RecordGap(NumDeclaredMethods(), n); + + bmtProp->fSparse = true; +#endif // FEATURE_COMINTEROP + continue; + } + + } + + + // + // This is a real method so add it to the enumeration of methods. We now need to retrieve + // information on the method and store it for later use. + // + if (FAILED(pMDInternalImport->GetMethodImplProps(tok, &dwMethodRVA, &dwImplFlags))) + { + BuildMethodTableThrowException(BFA_INVALID_TOKEN); + } + // + // But first - minimal flags validity checks + // + // No methods in Enums! + if (fIsClassEnum) + { + BuildMethodTableThrowException(BFA_METHOD_IN_A_ENUM); + } + // RVA : 0 + if (dwMethodRVA != 0) + { +#ifdef FEATURE_COMINTEROP + if(fIsClassComImport) + { + BuildMethodTableThrowException(BFA_METHOD_WITH_NONZERO_RVA); + } +#endif // FEATURE_COMINTEROP + if(IsMdAbstract(dwMemberAttrs)) + { + BuildMethodTableThrowException(BFA_ABSTRACT_METHOD_WITH_RVA); + } + if(IsMiRuntime(dwImplFlags)) + { + BuildMethodTableThrowException(BFA_RUNTIME_METHOD_WITH_RVA); + } + if(IsMiInternalCall(dwImplFlags)) + { + BuildMethodTableThrowException(BFA_INTERNAL_METHOD_WITH_RVA); + } + } + + // Abstract / not abstract + if(IsMdAbstract(dwMemberAttrs)) + { + if(fIsClassNotAbstract) + { + BuildMethodTableThrowException(BFA_AB_METHOD_IN_AB_CLASS); + } + if(!IsMdVirtual(dwMemberAttrs)) + { + BuildMethodTableThrowException(BFA_NONVIRT_AB_METHOD); + } + } + else if(fIsClassInterface && strMethodName && + (strcmp(strMethodName, COR_CCTOR_METHOD_NAME))) + { + BuildMethodTableThrowException(BFA_NONAB_NONCCTOR_METHOD_ON_INT); + } + + // Virtual / not virtual + if(IsMdVirtual(dwMemberAttrs)) + { + if(IsMdPinvokeImpl(dwMemberAttrs)) + { + BuildMethodTableThrowException(BFA_VIRTUAL_PINVOKE_METHOD); + } + if(IsMdStatic(dwMemberAttrs)) + { + BuildMethodTableThrowException(BFA_VIRTUAL_STATIC_METHOD); + } + if(strMethodName && (0==strcmp(strMethodName, COR_CTOR_METHOD_NAME))) + { + BuildMethodTableThrowException(BFA_VIRTUAL_INSTANCE_CTOR); + } + } + + // Some interface checks. + if (IsInterface()) + { + if (IsMdVirtual(dwMemberAttrs)) + { + if (!IsMdAbstract(dwMemberAttrs)) + { + BuildMethodTableThrowException(BFA_VIRTUAL_NONAB_INT_METHOD); + } + } + else + { + // Instance field/method + if (!IsMdStatic(dwMemberAttrs)) + { + BuildMethodTableThrowException(BFA_NONVIRT_INST_INT_METHOD); + } + } + } + + // No synchronized methods in ValueTypes + if(fIsClassValueType && IsMiSynchronized(dwImplFlags)) + { + BuildMethodTableThrowException(BFA_SYNC_METHOD_IN_VT); + } + + // Global methods: + if(IsGlobalClass()) + { + if(!IsMdStatic(dwMemberAttrs)) + { + BuildMethodTableThrowException(BFA_NONSTATIC_GLOBAL_METHOD); + } + if (strMethodName) //<TODO>@todo: investigate mc++ generating null name</TODO> + { + if(0==strcmp(strMethodName, COR_CTOR_METHOD_NAME)) + { + BuildMethodTableThrowException(BFA_GLOBAL_INST_CTOR); + } + } + } + //@GENERICS: + // Generic methods or methods in generic classes + // may not be part of a COM Import class, PInvoke, internal call. + if ((numGenericMethodArgs != 0) && + ( +#ifdef FEATURE_COMINTEROP + fIsClassComImport || + bmtProp->fComEventItfType || +#endif // FEATURE_COMINTEROP + IsMdPinvokeImpl(dwMemberAttrs) || + IsMiInternalCall(dwImplFlags))) + { + BuildMethodTableThrowException(BFA_BAD_PLACE_FOR_GENERIC_METHOD); + } + + // Generic methods may not be marked "runtime". However note that + // methods in generic delegate classes are, hence we don't apply this to + // methods in generic classes in general. + if (numGenericMethodArgs != 0 && IsMiRuntime(dwImplFlags)) + { + BuildMethodTableThrowException(BFA_GENERIC_METHOD_RUNTIME_IMPL); + } + + // Signature validation + if (FAILED(pMDInternalImport->GetSigOfMethodDef(tok,&cMemberSignature, &pMemberSignature))) + { + BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT); + } + hr = validateTokenSig(tok,pMemberSignature,cMemberSignature,dwMemberAttrs,pMDInternalImport); + if (FAILED(hr)) + { + BuildMethodTableThrowException(hr, BFA_BAD_SIGNATURE, mdMethodDefNil); + } + + // + // Determine the method's classification. + // + + if (IsReallyMdPinvokeImpl(dwMemberAttrs) || IsMiInternalCall(dwImplFlags)) + { + hr = NDirect::HasNAT_LAttribute(pMDInternalImport, tok, dwMemberAttrs); + + // There was a problem querying for the attribute + if (FAILED(hr)) + { + BuildMethodTableThrowException(hr, IDS_CLASSLOAD_BADPINVOKE, tok); + } + + // The attribute is not present + if (hr == S_FALSE) + { +#ifdef FEATURE_COMINTEROP + if (fIsClassComImport || bmtProp->fComEventItfType) + { + // tlbimported component + if (IsMdRTSpecialName(dwMemberAttrs)) + { + // constructor is special + Classification = mcFCall; + } + else + { + // Tlbimported components we have some + // method descs in the call which are just used + // for handling methodimpls of all interface methods + Classification = mcComInterop; + } + } + else +#endif // FEATURE_COMINTEROP + if (dwMethodRVA == 0) + Classification = mcFCall; + else + Classification = mcNDirect; + } + // The NAT_L attribute is present, marking this method as NDirect + else + { + CONSISTENCY_CHECK(hr == S_OK); + Classification = mcNDirect; + } + } + else if (IsMiRuntime(dwImplFlags)) + { + // currently the only runtime implemented functions are delegate instance methods + if (!IsDelegate() || IsMdStatic(dwMemberAttrs) || IsMdAbstract(dwMemberAttrs)) + { + BuildMethodTableThrowException(BFA_BAD_RUNTIME_IMPL); + } + + unsigned newDelegateMethodSeen = 0; + + if (IsMdRTSpecialName(dwMemberAttrs)) // .ctor + { + if (strcmp(strMethodName, COR_CTOR_METHOD_NAME) != 0 || IsMdVirtual(dwMemberAttrs)) + { + BuildMethodTableThrowException(BFA_BAD_FLAGS_ON_DELEGATE); + } + newDelegateMethodSeen = SeenCtor; + Classification = mcFCall; + } + else + { + if (strcmp(strMethodName, "Invoke") == 0) + newDelegateMethodSeen = SeenInvoke; + else if (strcmp(strMethodName, "BeginInvoke") == 0) + newDelegateMethodSeen = SeenBeginInvoke; + else if (strcmp(strMethodName, "EndInvoke") == 0) + newDelegateMethodSeen = SeenEndInvoke; + else + { + BuildMethodTableThrowException(BFA_UNKNOWN_DELEGATE_METHOD); + } + Classification = mcEEImpl; + } + + // If we get here we have either set newDelegateMethodSeen or we have thrown a BMT exception + _ASSERTE(newDelegateMethodSeen != 0); + + if ((delegateMethodsSeen & newDelegateMethodSeen) != 0) + { + BuildMethodTableThrowException(BFA_DUPLICATE_DELEGATE_METHOD); + } + + delegateMethodsSeen |= newDelegateMethodSeen; + } + else if (numGenericMethodArgs != 0) + { + //We use an instantiated method desc to represent a generic method + Classification = mcInstantiated; + } + else if (fIsClassInterface) + { +#ifdef FEATURE_COMINTEROP + if (IsMdStatic(dwMemberAttrs)) + { + // Static methods in interfaces need nothing special. + Classification = mcIL; + } + else if (bmtProp->fIsMngStandardItf) + { + // If the interface is a standard managed interface then allocate space for an FCall method desc. + Classification = mcFCall; + } + else + { + // If COM interop is supported then all other interface MDs may be + // accessed via COM interop <TODO> mcComInterop MDs are BIG - + // this is very often a waste of space </TODO> + Classification = mcComInterop; + } +#else // !FEATURE_COMINTEROP + // This codepath is used by remoting + Classification = mcIL; +#endif // !FEATURE_COMINTEROP + } + else + { + Classification = mcIL; + } + + +#ifdef _DEBUG + // We don't allow stack based declarative security on ecalls, fcalls and + // other special purpose methods implemented by the EE (the interceptor + // we use doesn't play well with non-jitted stubs). + if ((Classification == mcFCall || Classification == mcEEImpl) && + (IsMdHasSecurity(dwMemberAttrs) || IsTdHasSecurity(GetAttrClass()))) + { + DWORD dwSecFlags; + DWORD dwNullDeclFlags; + + if (IsTdHasSecurity(GetAttrClass()) && + SUCCEEDED(Security::GetDeclarationFlags(pMDInternalImport, GetCl(), &dwSecFlags, &dwNullDeclFlags))) + { + CONSISTENCY_CHECK_MSG(!(dwSecFlags & ~dwNullDeclFlags & DECLSEC_RUNTIME_ACTIONS), + "Cannot add stack based declarative security to a class containing an ecall/fcall/special method."); + } + if (IsMdHasSecurity(dwMemberAttrs) && + SUCCEEDED(Security::GetDeclarationFlags(pMDInternalImport, tok, &dwSecFlags, &dwNullDeclFlags))) + { + CONSISTENCY_CHECK_MSG(!(dwSecFlags & ~dwNullDeclFlags & DECLSEC_RUNTIME_ACTIONS), + "Cannot add stack based declarative security to an ecall/fcall/special method."); + } + } +#endif // _DEBUG + + // Generic methods should always be mcInstantiated + if (!((numGenericMethodArgs == 0) || ((Classification & mdcClassification) == mcInstantiated))) + { + BuildMethodTableThrowException(BFA_GENERIC_METHODS_INST); + } + // count how many overrides this method does All methods bodies are defined + // on this type so we can just compare the tok with the body token found + // from the overrides. + for(DWORD impls = 0; impls < bmtMethodImpl->dwNumberMethodImpls; impls++) { + if(bmtMethodImpl->rgMethodImplTokens[impls].methodBody == tok) { + Classification |= mdcMethodImpl; + break; + } + } + + // For delegates we don't allow any non-runtime implemented bodies + // for any of the four special methods + if (IsDelegate() && !IsMiRuntime(dwImplFlags)) + { + if ((strcmp(strMethodName, COR_CTOR_METHOD_NAME) == 0) || + (strcmp(strMethodName, "Invoke") == 0) || + (strcmp(strMethodName, "BeginInvoke") == 0) || + (strcmp(strMethodName, "EndInvoke") == 0) ) + { + BuildMethodTableThrowException(BFA_ILLEGAL_DELEGATE_METHOD); + } + } + + // + // Compute the type & other info + // + + // Set the index into the storage locations + BYTE impl; + if (Classification & mdcMethodImpl) + { + impl = METHOD_IMPL; + } + else + { + impl = METHOD_IMPL_NOT; + } + + BYTE type; + if ((Classification & mdcClassification) == mcNDirect) + { + type = METHOD_TYPE_NDIRECT; + } + else if ((Classification & mdcClassification) == mcFCall) + { + type = METHOD_TYPE_FCALL; + } + else if ((Classification & mdcClassification) == mcEEImpl) + { + type = METHOD_TYPE_EEIMPL; + } +#ifdef FEATURE_COMINTEROP + else if ((Classification & mdcClassification) == mcComInterop) + { + type = METHOD_TYPE_INTEROP; + } +#endif // FEATURE_COMINTEROP + else if ((Classification & mdcClassification) == mcInstantiated) + { + type = METHOD_TYPE_INSTANTIATED; + } + else + { + type = METHOD_TYPE_NORMAL; + } + + // + // Store the method and the information we have gathered on it in the metadata info structure. + // + + bmtMethod->SetMethodData(NumDeclaredMethods(), + tok, + dwMemberAttrs, + dwMethodRVA, + dwImplFlags, + Classification, + strMethodName, + impl, + type); + + IncNumDeclaredMethods(); + + // + // Update the count of the various types of methods. + // + + bmtVT->dwMaxVtableSize++; + } + + // Check to see that we have all of the required delegate methods (ECMA 13.6 Delegates) + if (IsDelegate()) + { + // Do we have all four special delegate methods + // or just the two special delegate methods + if ((delegateMethodsSeen != (SeenCtor | SeenInvoke | SeenBeginInvoke | SeenEndInvoke)) && + (delegateMethodsSeen != (SeenCtor | SeenInvoke)) ) + { + BuildMethodTableThrowException(BFA_MISSING_DELEGATE_METHOD); + } + } + + if (i != bmtMethod->cMethAndGaps) + { + BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_METHOD_COUNT, mdTokenNil); + } + + bmtMethod->hEnumMethod.EnumReset(); + +#ifdef FEATURE_COMINTEROP + // + // If the interface is sparse, we need to finalize the mapping list by + // telling it how many real methods we found. + // + + if (bmtProp->fSparse) + { + GetHalfBakedClass()->GetSparseCOMInteropVTableMap()->FinalizeMapping(NumDeclaredMethods()); + } +#endif // FEATURE_COMINTEROP +} + +#ifdef _PREFAST_ +#pragma warning(pop) +#endif + +//******************************************************************************* +// +// Used by BuildMethodTable +// +// Determines the maximum size of the vtable and allocates the temporary storage arrays +// Also copies the parent's vtable into the working vtable. +// +VOID MethodTableBuilder::AllocateMethodWorkingMemory() +{ + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(this)); + PRECONDITION(CheckPointer(bmtDomain)); + PRECONDITION(CheckPointer(bmtMethod)); + PRECONDITION(CheckPointer(bmtVT)); + PRECONDITION(CheckPointer(bmtInterface)); + PRECONDITION(CheckPointer(bmtParent)); + + } + CONTRACTL_END; + + DWORD i; + Thread *pThread = GetThread(); + + // Allocate a MethodDesc* for each method (needed later when doing interfaces), and a FieldDesc* for each field + bmtMethod->ppMethodDescList = new (&pThread->m_MarshalAlloc) MethodDesc*[NumDeclaredMethods()]; + ZeroMemory(bmtMethod->ppMethodDescList, NumDeclaredMethods() * sizeof(MethodDesc *)); + + // Create a temporary function table (we don't know how large the vtable will be until the very end, + // since duplicated interfaces are stored at the end of it). Calculate an upper bound. + // + // Upper bound is: The parent's class vtable size, plus every method declared in + // this class, plus the size of every interface we implement + // + // In the case of value classes, we add # InstanceMethods again, since we have boxed and unboxed versions + // of every vtable method. + // + if (IsValueClass()) + { + bmtVT->dwMaxVtableSize += NumDeclaredMethods(); + bmtMethod->ppUnboxMethodDescList = new (&pThread->m_MarshalAlloc) MethodDesc*[NumDeclaredMethods()]; + ZeroMemory(bmtMethod->ppUnboxMethodDescList, NumDeclaredMethods() * sizeof(MethodDesc*)); + } + + // sanity check + _ASSERTE(bmtParent->pParentMethodTable == NULL || + (bmtInterface->wInterfaceMapSize - bmtParent->pParentMethodTable->GetNumInterfaces()) >= 0); + + // add parent vtable size + bmtVT->dwMaxVtableSize += bmtVT->wCurrentVtableSlot; + + for (i = 0; i < bmtInterface->wInterfaceMapSize; i++) + { + // We double the interface size because we may end up duplicating the Interface for MethodImpls + bmtVT->dwMaxVtableSize += (bmtInterface->pInterfaceMap[i].m_pMethodTable->GetNumVirtuals() * 2); + } + + // Allocate the temporary vtable + bmtVT->pVtable = new (&pThread->m_MarshalAlloc)PCODE [bmtVT->dwMaxVtableSize]; + ZeroMemory(bmtVT->pVtable, bmtVT->dwMaxVtableSize * sizeof(PCODE)); + bmtVT->pVtableMD = new (&pThread->m_MarshalAlloc) MethodDesc*[bmtVT->dwMaxVtableSize]; + ZeroMemory(bmtVT->pVtableMD, bmtVT->dwMaxVtableSize * sizeof(MethodDesc*)); + + // Allocate the temporary non-vtable + bmtVT->pNonVtableMD = new (&pThread->m_MarshalAlloc) MethodDesc*[NumDeclaredMethods()]; + ZeroMemory(bmtVT->pNonVtableMD, sizeof(MethodDesc*) * NumDeclaredMethods()); + + if (bmtParent->pParentMethodTable != NULL) + { + // Copy parent's vtable into our "temp" vtable + { + MethodTable::MethodIterator it(bmtParent->pParentMethodTable); + for (;it.IsValid() && it.IsVirtual(); it.Next()) { + DWORD slot = it.GetSlotNumber(); + bmtVT->pVtable[slot] = it.GetTarget().GetTarget(); + bmtVT->pVtableMD[slot] = NULL; // MethodDescs are resolved lazily + } + bmtVT->pParentMethodTable = bmtParent->pParentMethodTable; + } + +#if 0 + // @<TODO>todo: Figure out the right way to override Equals for value + // types only. + // + // This is broken because + // (a) g_pObjectClass->FindMethod("Equals", &gsig_IM_Obj_RetBool); will return + // the EqualsValue method + // (b) When mscorlib has been preloaded (and thus the munge already done + // ahead of time), we cannot easily find both methods + // to compute EqualsAddr & EqualsSlot + // + // For now, the Equals method has a runtime check to see if it's + // comparing value types. + //</TODO> + + // If it is a value type, over ride a few of the base class methods. + if (IsValueClass()) + { + static WORD EqualsSlot; + + // If we haven't been through here yet, get some stuff from the Object class definition. + if (EqualsSlot == NULL) + { + // Get the slot of the Equals method. + MethodDesc *pEqualsMD = g_pObjectClass->FindMethod("Equals", &gsig_IM_Obj_RetBool); + THROW_BAD_FORMAT_MAYBE(pEqualsMD != NULL, 0, this); + EqualsSlot = pEqualsMD->GetSlot(); + + // Get the address of the EqualsValue method. + MethodDesc *pEqualsValueMD = g_pObjectClass->FindMethod("EqualsValue", &gsig_IM_Obj_RetBool); + THROW_BAD_FORMAT_MAYBE(pEqualsValueMD != NULL, 0, this); + + // Patch the EqualsValue method desc in a dangerous way to + // look like the Equals method desc. + pEqualsValueMD->SetSlot(EqualsSlot); + pEqualsValueMD->SetMemberDef(pEqualsMD->GetMemberDef()); + } + + // Override the valuetype "Equals" with "EqualsValue". + bmtVT->SetMethodDescForSlot(EqualsSlot, EqualsSlot); + } +#endif // 0 + } + + if (NumDeclaredMethods() > 0) + { + bmtParent->ppParentMethodDescBuf = (MethodDesc **) + pThread->m_MarshalAlloc.Alloc(S_UINT32(2) * S_UINT32(NumDeclaredMethods()) * + S_UINT32(sizeof(MethodDesc*))); + + bmtParent->ppParentMethodDescBufPtr = bmtParent->ppParentMethodDescBuf; + } +} + +//******************************************************************************* +// +// Find a method in this class hierarchy - used ONLY by the loader during layout. Do not use at runtime. +// +// *ppMemberSignature must be NULL on entry - it and *pcMemberSignature may or may not be filled out +// +// ppMethodDesc will be filled out with NULL if no matching method in the hierarchy is found. +// +// Returns FALSE if there was an error of some kind. +// +// pMethodConstraintsMatch receives the result of comparing the method constraints. +HRESULT MethodTableBuilder::LoaderFindMethodInClass( + LPCUTF8 pszMemberName, + Module* pModule, + mdMethodDef mdToken, + MethodDesc ** ppMethodDesc, + PCCOR_SIGNATURE * ppMemberSignature, + DWORD * pcMemberSignature, + DWORD dwHashName, + BOOL * pMethodConstraintsMatch) +{ + CONTRACTL + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(this)); + PRECONDITION(CheckPointer(bmtParent)); + PRECONDITION(CheckPointer(pModule)); + PRECONDITION(CheckPointer(ppMethodDesc)); + PRECONDITION(CheckPointer(ppMemberSignature)); + PRECONDITION(CheckPointer(pcMemberSignature)); + } + CONTRACTL_END; + + HRESULT hr; + MethodHashEntry *pEntry; + DWORD dwNameHashValue; + + _ASSERTE(pModule); + _ASSERTE(*ppMemberSignature == NULL); + + // No method found yet + *ppMethodDesc = NULL; + + // Have we created a hash of all the methods in the class chain? + if (bmtParent->pParentMethodHash == NULL) + { + // There may be such a method, so we will now create a hash table to reduce the pain for + // further lookups + + // <TODO> Are we really sure that this is worth doing? </TODO> + bmtParent->pParentMethodHash = CreateMethodChainHash(bmtParent->pParentMethodTable); + } + + // Look to see if the method exists in the parent hash + pEntry = bmtParent->pParentMethodHash->Lookup(pszMemberName, dwHashName); + if (pEntry == NULL) + { + return S_OK; // No method by this name exists in the hierarchy + } + + // Get signature of the method we're searching for - we will need this to verify an exact name-signature match + IfFailRet(pModule->GetMDImport()->GetSigOfMethodDef( + mdToken, + pcMemberSignature, + ppMemberSignature)); + + // Hash value we are looking for in the chain + dwNameHashValue = pEntry->m_dwHashValue; + + // We've found a method with the same name, but the signature may be different + // Traverse the chain of all methods with this name + while (1) + { + PCCOR_SIGNATURE pHashMethodSig = NULL; + DWORD cHashMethodSig = 0; + Substitution * pSubst = NULL; + MethodDesc * entryDesc = pEntry->m_pDesc; + MethodTable * entryMT = entryDesc->GetMethodTable(); + MethodTable * entryCanonMT = entryMT->GetCanonicalMethodTable(); + + // If entry is in a parameterized type, its signature may need to be instantiated all the way down the chain + // To understand why consider the following example: + // class C<T> { void m(T) { ...body... } } + // class D<T> : C<T[]> { /* inherits m with signature void m(T[]) */ } + // class E<T> : D<List<T>> { void m(List<T>[]) { ... body... } } + // Now suppose that we've got the signature of E::m in our hand and are comparing it with the methoddesc for C.m + // They're not syntactically the same but are if you instantiate "all the way up" + // Possible optimization: don't bother constructing the substitution if the signature of pEntry is closed + if (entryCanonMT->GetNumGenericArgs() > 0) + { + MethodTable *here = GetHalfBakedMethodTable(); + _ASSERTE(here->GetModule()); + MethodTable *pParent = bmtParent->pParentMethodTable; + + for (;;) + { + Substitution *newSubst = new Substitution; + *newSubst = here->GetSubstitutionForParent(pSubst); + pSubst = newSubst; + + here = pParent->GetCanonicalMethodTable(); + if (entryCanonMT == here) + break; + pParent = pParent->GetParentMethodTable(); + _ASSERT(pParent != NULL); + } + } + + // Get sig of entry in hash chain + entryDesc->GetSig(&pHashMethodSig, &cHashMethodSig); + + // Note instantiation info + { + hr = MetaSig::CompareMethodSigsNT(*ppMemberSignature, *pcMemberSignature, pModule, NULL, + pHashMethodSig, cHashMethodSig, entryDesc->GetModule(), pSubst); + + if (hr == S_OK) + { // Found a match + *ppMethodDesc = entryDesc; + // Check the constraints are consistent, + // and return the result to the caller. + // We do this here to avoid recalculating pSubst. + *pMethodConstraintsMatch = + MetaSig::CompareMethodConstraints(NULL, pModule, mdToken, pSubst, + entryDesc->GetModule(), + entryDesc->GetMemberDef()); + } + + if (pSubst != NULL) + { + pSubst->DeleteChain(); + pSubst = NULL; + } + + if (FAILED(hr) || hr == S_OK) + { + return hr; + } + } + + do + { // Advance to next item in the hash chain which has the same name + pEntry = pEntry->m_pNext; // Next entry in the hash chain + + if (pEntry == NULL) + { + return S_OK; // End of hash chain, no match found + } + } while ((pEntry->m_dwHashValue != dwNameHashValue) || (strcmp(pEntry->m_pKey, pszMemberName) != 0)); + } + + return S_OK; +} + +//******************************************************************************* +// +// Find a method declaration that must reside in the scope passed in. This method cannot be called if +// the reference travels to another scope. +// +// Protect against finding a declaration that lives within +// us (the type being created) +// + +HRESULT MethodTableBuilder::FindMethodDeclarationForMethodImpl( + IMDInternalImport * pMDInternalImport, // Scope in which tkClass and tkMethod are defined. + mdTypeDef tkClass, // Type that the method def resides in + mdToken tkMethod, // Token that is being located (MemberRef or MethodDef) + mdMethodDef * ptkMethodDef) // Method definition for Member +{ + STANDARD_VM_CONTRACT; + + HRESULT hr = S_OK; + + PCCOR_SIGNATURE pSig; // Signature of Member + DWORD cSig; + LPCUTF8 szMember = NULL; + // The token should be a member ref or def. If it is a ref then we need to travel + // back to us hopefully. + if(TypeFromToken(tkMethod) == mdtMemberRef) + { + // Get the parent + mdToken typeref; + if (FAILED(pMDInternalImport->GetParentOfMemberRef(tkMethod, &typeref))) + { + BAD_FORMAT_NOTHROW_ASSERT(!"Invalid MemberRef record"); + IfFailRet(COR_E_TYPELOAD); + } + + while (TypeFromToken(typeref) == mdtTypeSpec) + { + // Added so that method impls can refer to instantiated interfaces or classes + if (FAILED(pMDInternalImport->GetSigFromToken(typeref, &cSig, &pSig))) + { + BAD_FORMAT_NOTHROW_ASSERT(!"Invalid TypeSpec record"); + IfFailRet(COR_E_TYPELOAD); + } + CorElementType elemType = (CorElementType) *pSig++; + + // If this is a generic inst, we expect that the next elem is ELEMENT_TYPE_CLASS, + // which is handled in the case below. + if (elemType == ELEMENT_TYPE_GENERICINST) + { + elemType = (CorElementType) *pSig++; + BAD_FORMAT_NOTHROW_ASSERT(elemType == ELEMENT_TYPE_CLASS); + } + + // This covers E_T_GENERICINST and E_T_CLASS typespec formats. We don't expect + // any other kinds to come through here. + if (elemType == ELEMENT_TYPE_CLASS) + { + CorSigUncompressToken(pSig, &typeref); + } + else + { + // This is an unrecognized signature format. + BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, + IDS_CLASSLOAD_MI_BAD_SIG, + mdMethodDefNil); + } + } + + // If parent is a method def then this is a varags method + if (TypeFromToken(typeref) == mdtMethodDef) + { + mdTypeDef typeDef; + hr = pMDInternalImport->GetParentToken(typeref, &typeDef); + + // Make sure it is a typedef + if (TypeFromToken(typeDef) != mdtTypeDef) + { + BAD_FORMAT_NOTHROW_ASSERT(!"MethodDef without TypeDef as Parent"); + IfFailRet(COR_E_TYPELOAD); + } + BAD_FORMAT_NOTHROW_ASSERT(typeDef == tkClass); + // This is the real method we are overriding + // <TODO>@TODO: CTS this may be illegal and we could throw an error</TODO> + *ptkMethodDef = typeref; + } + + else + { + // Verify that the ref points back to us + mdToken tkDef = mdTokenNil; + + // We only get here when we know the token does not reference a type + // in a different scope. + if(TypeFromToken(typeref) == mdtTypeRef) + { + LPCUTF8 pszNameSpace; + LPCUTF8 pszClassName; + + if (FAILED(pMDInternalImport->GetNameOfTypeRef(typeref, &pszNameSpace, &pszClassName))) + { + IfFailRet(COR_E_TYPELOAD); + } + mdToken tkRes; + if (FAILED(pMDInternalImport->GetResolutionScopeOfTypeRef(typeref, &tkRes))) + { + IfFailRet(COR_E_TYPELOAD); + } + hr = pMDInternalImport->FindTypeDef(pszNameSpace, + pszClassName, + (TypeFromToken(tkRes) == mdtTypeRef) ? tkRes : mdTokenNil, + &tkDef); + if(FAILED(hr)) + { + IfFailRet(COR_E_TYPELOAD); + } + } + + // We get a typedef when the parent of the token is a typespec to the type. + else if (TypeFromToken(typeref) == mdtTypeDef) + { + tkDef = typeref; + } + + else + { + CONSISTENCY_CHECK_MSGF(FALSE, ("Invalid methodimpl signature in class %s.", GetDebugClassName())); + BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, + IDS_CLASSLOAD_MI_BAD_SIG, + mdMethodDefNil); + } + + // If we required that the typedef be the same type as the current class, + // and it doesn't match, we need to return a failure result. + if (tkDef != tkClass) + { + IfFailRet(COR_E_TYPELOAD); + } + + IfFailRet(pMDInternalImport->GetNameAndSigOfMemberRef(tkMethod, &pSig, &cSig, &szMember)); + + if (isCallConv( + MetaSig::GetCallingConvention(NULL, Signature(pSig, cSig)), + IMAGE_CEE_CS_CALLCONV_FIELD)) + { + return VLDTR_E_MR_BADCALLINGCONV; + } + + hr = pMDInternalImport->FindMethodDef( + tkDef, szMember, pSig, cSig, ptkMethodDef); + IfFailRet(hr); + } + } + + else if (TypeFromToken(tkMethod) == mdtMethodDef) + { + mdTypeDef typeDef; + + // Verify that we are the parent + hr = pMDInternalImport->GetParentToken(tkMethod, &typeDef); + IfFailRet(hr); + + if(typeDef != tkClass) + { + IfFailRet(COR_E_TYPELOAD); + } + + *ptkMethodDef = tkMethod; + } + + else + { + IfFailRet(COR_E_TYPELOAD); + } + + return hr; +} + +//******************************************************************************* +void MethodTableBuilder::bmtMethodImplInfo::AddMethod(MethodDesc* pImplDesc, MethodDesc* pDesc, mdToken mdDecl, Substitution *pDeclSubst) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE((pDesc == NULL || mdDecl == mdTokenNil) && (pDesc != NULL || mdDecl != mdTokenNil)); + rgEntries[pIndex].pDeclDesc = pDesc; + rgEntries[pIndex].declToken = mdDecl; + rgEntries[pIndex].declSubst = *pDeclSubst; + rgEntries[pIndex].pBodyDesc = pImplDesc; + pIndex++; +} + +//******************************************************************************* +// Returns TRUE if tok acts as a body for any methodImpl entry. FALSE, otherwise. +BOOL MethodTableBuilder::bmtMethodImplInfo::IsBody(mdToken tok) +{ + LIMITED_METHOD_CONTRACT; + CONSISTENCY_CHECK(TypeFromToken(tok) == mdtMethodDef); + for (DWORD i = 0; i < pIndex; i++) { + if (GetBodyMethodDesc(i)->GetMemberDef() == tok) { + return TRUE; + } + } + return FALSE; +} + +//******************************************************************************* +// Returns TRUE for success, FALSE for failure +void MethodNameHash::Init(DWORD dwMaxEntries, StackingAllocator *pAllocator) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + PRECONDITION(CheckPointer(this)); + } + CONTRACTL_END; + + // Given dwMaxEntries, determine a good value for the number of hash buckets + m_dwNumBuckets = (dwMaxEntries / 10); + + if (m_dwNumBuckets < 5) + m_dwNumBuckets = 5; + + S_UINT32 scbMemory = (S_UINT32(m_dwNumBuckets) * S_UINT32(sizeof(MethodHashEntry*))) + + (S_UINT32(dwMaxEntries) * S_UINT32(sizeof(MethodHashEntry))); + + if (scbMemory.IsOverflow()) + { + ThrowHR(E_INVALIDARG); + } + + if (pAllocator) + { + m_pMemoryStart = (BYTE*)pAllocator->Alloc(scbMemory); + } + else + { // We're given the number of hash table entries we're going to insert, + // so we can allocate the appropriate size + m_pMemoryStart = new BYTE[scbMemory.Value()]; + } + + INDEBUG(m_pDebugEndMemory = m_pMemoryStart + scbMemory.Value();) + + // Current alloc ptr + m_pMemory = m_pMemoryStart; + + // Allocate the buckets out of the alloc ptr + m_pBuckets = (MethodHashEntry**) m_pMemory; + m_pMemory += sizeof(MethodHashEntry*)*m_dwNumBuckets; + + // Buckets all point to empty lists to begin with + memset(m_pBuckets, 0, scbMemory.Value()); +} + +//******************************************************************************* +// Insert new entry at head of list +void MethodNameHash::Insert(LPCUTF8 pszName, MethodDesc *pDesc) +{ + LIMITED_METHOD_CONTRACT; + DWORD dwHash = HashStringA(pszName); + DWORD dwBucket = dwHash % m_dwNumBuckets; + MethodHashEntry*pNewEntry; + + pNewEntry = (MethodHashEntry *) m_pMemory; + m_pMemory += sizeof(MethodHashEntry); + + _ASSERTE(m_pMemory <= m_pDebugEndMemory); + + // Insert at head of bucket chain + pNewEntry->m_pNext = m_pBuckets[dwBucket]; + pNewEntry->m_pDesc = pDesc; + pNewEntry->m_dwHashValue = dwHash; + pNewEntry->m_pKey = pszName; + + m_pBuckets[dwBucket] = pNewEntry; +} + +//******************************************************************************* +// Return the first MethodHashEntry with this name, or NULL if there is no such entry +MethodHashEntry *MethodNameHash::Lookup(LPCUTF8 pszName, DWORD dwHash) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_FORBID_FAULT; + + + if (!dwHash) + dwHash = HashStringA(pszName); + DWORD dwBucket = dwHash % m_dwNumBuckets; + MethodHashEntry*pSearch; + + for (pSearch = m_pBuckets[dwBucket]; pSearch; pSearch = pSearch->m_pNext) + { + if (pSearch->m_dwHashValue == dwHash && !strcmp(pSearch->m_pKey, pszName)) + return pSearch; + } + + return NULL; +} + +//******************************************************************************* +// +// Create a hash of all methods in this class. The hash is from method name to MethodDesc. +// +MethodNameHash *MethodTableBuilder::CreateMethodChainHash(MethodTable *pMT) +{ + STANDARD_VM_CONTRACT; + + Thread *pThread = GetThread(); + MethodNameHash *pHash = new (&pThread->m_MarshalAlloc) MethodNameHash(); + + pHash->Init(pMT->GetNumVirtuals(), &(pThread->m_MarshalAlloc)); + + MethodTable::MethodIterator it(pMT); + for (;it.IsValid(); it.Next()) + { + if (it.IsVirtual()) + { + MethodDesc *pImplDesc = it.GetMethodDesc(); + CONSISTENCY_CHECK(CheckPointer(pImplDesc)); + MethodDesc *pDeclDesc = it.GetDeclMethodDesc(); + CONSISTENCY_CHECK(CheckPointer(pDeclDesc)); + + CONSISTENCY_CHECK(pMT->IsInterface() || !pDeclDesc->IsInterface()); + pHash->Insert(pDeclDesc->GetNameOnNonArrayClass(), pDeclDesc); + } + } + + // Success + return pHash; +} + +//******************************************************************************* +void MethodTableBuilder::SetBMTData( + BaseDomain *bmtDomain, + bmtErrorInfo *bmtError, + bmtProperties *bmtProp, + bmtVtable *bmtVT, + bmtParentInfo *bmtParent, + bmtInterfaceInfo *bmtInterface, + bmtMethodInfo *bmtMethod, + bmtTypeInfo *bmtType, + bmtMethodImplInfo *bmtMethodImpl) +{ + LIMITED_METHOD_CONTRACT; + this->bmtDomain = bmtDomain; + this->bmtError = bmtError; + this->bmtProp = bmtProp; + this->bmtVT = bmtVT; + this->bmtParent = bmtParent; + this->bmtInterface = bmtInterface; + this->bmtMethod = bmtMethod; + this->bmtType = bmtType; + this->bmtMethodImpl = bmtMethodImpl; +} + +//******************************************************************************* +void MethodTableBuilder::NullBMTData() +{ + LIMITED_METHOD_CONTRACT; + this->bmtDomain = NULL; + this->bmtError = NULL; + this->bmtProp = NULL; + this->bmtVT = NULL; + this->bmtParent = NULL; + this->bmtInterface = NULL; + this->bmtMethod = NULL; + this->bmtType = NULL; + this->bmtMethodImpl = NULL; +} + +//******************************************************************************* +/*static*/ +VOID DECLSPEC_NORETURN MethodTableBuilder::BuildMethodTableThrowException( + HRESULT hr, + const bmtErrorInfo & bmtError) +{ + STANDARD_VM_CONTRACT; + + LPCUTF8 pszClassName, pszNameSpace; + if (FAILED(bmtError.pModule->GetMDImport()->GetNameOfTypeDef(bmtError.cl, &pszClassName, &pszNameSpace))) + { + pszClassName = pszNameSpace = "Invalid TypeDef record"; + } + + if (IsNilToken(bmtError.dMethodDefInError) && bmtError.szMethodNameForError == NULL) { + if (hr == E_OUTOFMEMORY) + COMPlusThrowOM(); + else + bmtError.pModule->GetAssembly()->ThrowTypeLoadException(pszNameSpace, pszClassName, + bmtError.resIDWhy); + } + else { + LPCUTF8 szMethodName; + if (bmtError.szMethodNameForError == NULL) + { + if (FAILED((bmtError.pModule->GetMDImport())->GetNameOfMethodDef(bmtError.dMethodDefInError, &szMethodName))) + { + szMethodName = "Invalid MethodDef record"; + } + } + else + szMethodName = bmtError.szMethodNameForError; + + bmtError.pModule->GetAssembly()->ThrowTypeLoadException(pszNameSpace, pszClassName, + szMethodName, bmtError.resIDWhy); + } + +} + +//******************************************************************************* +/* static */ +int __cdecl MethodTableBuilder::bmtMethodImplInfo::MethodImplTokenPair::Compare( + const void *elem1, + const void *elem2) +{ + STATIC_CONTRACT_LEAF; + MethodImplTokenPair *e1 = (MethodImplTokenPair *)elem1; + MethodImplTokenPair *e2 = (MethodImplTokenPair *)elem2; + if (e1->methodBody < e2->methodBody) return -1; + else if (e1->methodBody > e2->methodBody) return 1; + else if (e1->methodDecl < e2->methodDecl) return -1; + else if (e1->methodDecl > e2->methodDecl) return 1; + else return 0; +} + +//******************************************************************************* +/* static */ +BOOL MethodTableBuilder::bmtMethodImplInfo::MethodImplTokenPair::Equal( + const MethodImplTokenPair *elem1, + const MethodImplTokenPair *elem2) +{ + STATIC_CONTRACT_LEAF; + return ((elem1->methodBody == elem2->methodBody) && + (elem1->methodDecl == elem2->methodDecl)); +} + + +}; // namespace ClassCompat + +#endif // !DACCESS_COMPILE |