// 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 "jitinterface.h" #include "eeconfig.h" #include "log.h" #include "fieldmarshaler.h" #include "cgensys.h" #include "gcheaputilities.h" #include "dbginterface.h" #include "comdelegate.h" #include "sigformat.h" #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 "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; // Verify that the substitution doesn't change for this case 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. Currently we don't fail, but @TODO perhaps we should fail if we recurse // too much. // 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. // // we should document the reasons why. One reason is that DispatchMapTypeIDs can be indexes // into the list 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; // 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); } } #ifndef FEATURE_DEFAULT_INTERFACES // Some interface checks. if (fIsClassInterface) { 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); } } } #endif // 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: investigate mc++ generating null name { 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 (IsMdAbstract(dwMemberAttrs)) { // If COM interop is supported then all other interface MDs may be // accessed via COM interop mcComInterop MDs are BIG - // this is very often a waste of space // @DIM_TODO - What if default interface method is called through COM interop? Classification = mcComInterop; } else #endif // !FEATURE_COMINTEROP { // This codepath is used by remoting and default interface methods Classification = mcIL; } } else { Classification = mcIL; } // 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: 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. // // 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 // Are we really sure that this is worth doing? 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 { void m(T) { ...body... } } // class D : C { /* inherits m with signature void m(T[]) */ } // class E : D> { void m(List[]) { ... 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: CTS this may be illegal and we could throw an error *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