// ==++==
//
//
// Copyright (c) 2006 Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are contained in the file
// named license.txt, which can be found in the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by the
// terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
// ==--==
// ===========================================================================
// File: CLASS.CPP
//
#include "common.h"
#include "clsload.hpp"
#include "method.hpp"
#include "class.h"
#include "class.inl"
#include "object.h"
#include "field.h"
#include "util.hpp"
#include "excep.h"
#include "siginfo.hpp"
#include "threads.h"
#include "stublink.h"
#include "ecall.h"
#include "dllimport.h"
#include "gcdesc.h"
#include "verifier.hpp"
#include "jitinterface.h"
#include "eeconfig.h"
#include "log.h"
#include "fieldmarshaler.h"
#include "cgensys.h"
#include "gc.h"
#include "security.h"
#include "comstringbuffer.h"
#include "dbginterface.h"
#include "comdelegate.h"
#include "sigformat.h"
#include "remoting.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 "objectclone.h"
#include "mdaassistantsptr.h"
#include "listlock.inl"
#include "generics.h"
#include "genericdict.h"
#include "instmethhash.h"
#include "typeparse.h"
#include "typestring.h"
#include "typedesc.h"
#include "ecmakey.h"
#include "constrainedexecutionregion.h"
#include "security.inl"
#ifndef DACCESS_COMPILE
//*******************************************************************************
// Helper functions to sort GCdescs by offset (decending order)
int __cdecl compareCGCDescSeries(const void *arg1, const void *arg2)
{
STATIC_CONTRACT_NOTHROW;
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_FORBID_FAULT;
CGCDescSeries* gcInfo1 = (CGCDescSeries*) arg1;
CGCDescSeries* gcInfo2 = (CGCDescSeries*) arg2;
return (int)(gcInfo2->GetSeriesOffset() - gcInfo1->GetSeriesOffset());
}
#define RVA_FIELD_VALIDATION_ENABLED
#define UNPLACED_NONVTABLE_SLOT_NUMBER ((WORD) -2)
#include "assembly.hpp"
char* FormatSig(MethodDesc* pMD, BaseDomain *pDomain, AllocMemTracker *pamTracker);
//
// The MethodNameHash is a temporary loader structure which may be allocated if there are a large number of
// methods in a class, to quickly get from a method name to a MethodDesc (potentially a chain of MethodDescs).
//
//*******************************************************************************
// 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;
unsigned cbMemory = 0;
unsigned cbEntries = 0;
if (!ClrSafeInt<unsigned>::multiply(m_dwNumBuckets, sizeof(MethodHashEntry*), cbMemory) ||
!ClrSafeInt<unsigned>::multiply(dwMaxEntries, sizeof(MethodHashEntry), cbEntries) ||
!ClrSafeInt<unsigned>::addition(cbMemory, cbEntries, cbMemory))
ThrowHR(E_INVALIDARG);
if (pAllocator)
{
m_pMemoryStart = (BYTE*)pAllocator->Alloc(cbMemory);
}
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[cbMemory];
}
INDEBUG(m_pDebugEndMemory = m_pMemoryStart + cbMemory;)
// 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, sizeof(MethodHashEntry*)*m_dwNumBuckets);
}
//*******************************************************************************
// Insert new entry at head of list
void MethodNameHash::Insert(LPCUTF8 pszName, MethodDesc *pDesc)
{
LEAF_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;
}
#define MAX(a,b) (((a)>(b))?(a):(b))
#ifdef _DEBUG
unsigned g_dupMethods = 0;
unsigned g_numMethods = 0;
#endif // _DEBUG
// Define this to cause all vtable and field information to be dumped to the screen
//#define FULL_DEBUG
//*******************************************************************************
EEClass::EEClass(Module *pModule, DWORD genericsFlags)
{
LEAF_CONTRACT;
m_VMFlags = 0;
m_pModule = pModule;
_ASSERTE(pModule != NULL);
m_pMethodTable = NULL;
// Set the generics flags early so we can always determine whether this is an EEClass for an
// instantiated type, e.g. List<int> or List<object>. This helps
// us sanity check things during class creation.
//
m_VMFlags |= genericsFlags;
// Set the interface ID to -1 to indicate it hasn't been set yet.
m_cbModuleDynamicID = (DWORD) -1;
#ifdef _DEBUG
m_szDebugClassName = NULL;
m_fDebuggingClass = FALSE;
#endif // _DEBUG
#if CHECK_APP_DOMAIN_LEAKS
m_wAuxFlags = 0;
#endif
}
//*******************************************************************************
void *EEClass::operator new(size_t size, size_t extraSize, LoaderHeap *pHeap, Module *pModule, size_t *dwSizeRequestedForAlloc, AllocMemTracker *pamTracker)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
INJECT_FAULT(COMPlusThrowOM());
}
CONTRACTL_END
if(extraSize!=0)
{
if (size+extraSize<size)
ThrowHR(COR_E_OVERFLOW);
size+=extraSize;
}
#ifdef _DEBUG
pModule->GetClassLoader()->m_dwEEClassData += size;
#endif
// Make sure that the EEClass is ptr size aligned.
// This ensures that the buckets that follow are also aligned.
_ASSERTE(size == ALIGN_UP(size, sizeof(UINT_PTR)));
size = ALIGN_UP(size, sizeof(UINT_PTR));
void *pTmp;
*dwSizeRequestedForAlloc = size; // Must give caller our alloc size so he can call BackoutMem
pTmp = pamTracker->Track(pHeap->AllocMem_NoThrow(size));
if (!pTmp)
{
COMPlusThrowOM();
}
// No need to memset since this memory came from VirtualAlloc'ed memory
// memset (pTmp, 0, size);
return pTmp;
}
//*******************************************************************************
void EEClass::Destruct()
{
CONTRACTL
{
NOTHROW;
GC_TRIGGERS;
FORBID_FAULT;
}
CONTRACTL_END
// If we haven't been restored, we can ignore the class
if (GetMethodTable() && !GetMethodTable()->IsRestored())
return;
SetDestroyed();
#ifdef PROFILING_SUPPORTED
// If profiling, then notify the class is getting unloaded.
TypeID clsId = NULL;
if (CORProfilerTrackClasses() && !IsArrayClass() && GetMethodTable())
{
//
//
FAULT_NOT_FATAL();
EX_TRY
{
PROFILER_CALL;
g_profControlBlock.pProfInterface->ClassUnloadStarted(
(ThreadID) GetThread(),
clsId = (TypeID) TypeHandle(this->GetMethodTable()).AsPtr());
}
EX_CATCH
{
// The exception here came from the profiler itself. We'll just
// swallow the exception, since we don't want the profiler to bring
// down the runtime.
}
EX_END_CATCH(RethrowTerminalExceptions);
}
#endif // PROFILING_SUPPORTED
if (IsAnyDelegateClass())
{
DelegateEEClass* pDelegateEEClass = (DelegateEEClass*)this;
if (pDelegateEEClass->m_pSecurityStub)
{
Stub* pSecurityStub = pDelegateEEClass->m_pSecurityStub;
pSecurityStub->DecRef();
Stub* pInterceptedStub = *(((InterceptStub*)pSecurityStub)->GetInterceptedStub());
if (pInterceptedStub == pDelegateEEClass->m_pUMCallStub)
{
pDelegateEEClass->m_pUMCallStub = NULL;
}
else if (pInterceptedStub == pDelegateEEClass->m_pMLStub)
{
pDelegateEEClass->m_pMLStub = NULL;
}
}
if (pDelegateEEClass->m_pStaticCallStub)
{
BOOL fStubDeleted = pDelegateEEClass->m_pStaticCallStub->DecRef();
if (fStubDeleted)
{
DelegateInvokeStubManager::g_pManager->RemoveStub(pDelegateEEClass->m_pStaticCallStub);
}
}
if (pDelegateEEClass->m_pUMCallStub)
{
pDelegateEEClass->m_pUMCallStub->DecRef();
}
if (pDelegateEEClass->m_pInstRetBuffCallStub)
{
pDelegateEEClass->m_pInstRetBuffCallStub->DecRef();
}
if (pDelegateEEClass->m_pMLStub)
{
pDelegateEEClass->m_pMLStub->DecRef();
}
// While m_pMultiCastInvokeStub is also a member,
// it is owned by the m_pMulticastStubCache, not by the class
// - it is shared across classes. So we don't decrement
// its ref count here
delete pDelegateEEClass->m_pUMThunkMarshInfo;
}
if (GetMethodTable() && GetMethodTable()->IsThunking())
{
GetMethodTable()->MarkAsNotThunking();
}
// Destruct the method descs by walking the chunks.
DWORD i, n;
MethodDescChunk *pChunk = GetChunks();
// If we haven't created a methodtable yet, we better not have created any chunks!
_ASSERTE(GetMethodTable() || !pChunk);
while (pChunk != NULL)
{
n = pChunk->GetCount();
for (i = 0; i < n; i++)
{
MethodDesc *pMD = pChunk->GetMethodDescAt(i);
pMD->Destruct();
}
pChunk = pChunk->GetNextChunk();
}
#ifdef PROFILING_SUPPORTED
// If profiling, then notify the class is getting unloaded.
if (CORProfilerTrackClasses() &&
!IsArrayClass() &&
// If there was an exception while the type was being loaded,
// clsId may not be set. No need to do the callback in that case.
clsId)
{
// See comments in the call to ClassUnloadStarted for details on this
// FAULT_NOT_FATAL marker and exception swallowing.
FAULT_NOT_FATAL();
EX_TRY
{
PROFILER_CALL;
g_profControlBlock.pProfInterface->ClassUnloadFinished((ThreadID) GetThread(), clsId, S_OK);
}
EX_CATCH
{
}
EX_END_CATCH(RethrowTerminalExceptions);
}
#endif // PROFILING_SUPPORTED
}
//*******************************************************************************
EEClassLayoutInfo *EEClass::GetLayoutInfo()
{
STATIC_CONTRACT_NOTHROW;
STATIC_CONTRACT_GC_NOTRIGGER;
STATIC_CONTRACT_FORBID_FAULT;
STATIC_CONTRACT_SO_TOLERANT;
_ASSERTE(HasLayout());
g_IBCLogger.LogEEClassAndMethodTableAccess(this);
return &((LayoutEEClass *) this)->m_LayoutInfo;
}
void MethodTableBuilder::CreateMinimalClass(LoaderHeap *pHeap,
Module* pModule,
AllocMemTracker *pamTracker,
SIZE_T cbExtra,
EEClass** ppEEClass)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
PRECONDITION(ppEEClass!=NULL);
}
CONTRACTL_END;
size_t cbSize;
*ppEEClass = new (cbExtra,pHeap, pModule, &cbSize, pamTracker) EEClass(pModule, 0);
}
//==========================================================================
// This function is very specific about how it constructs a EEClass. It first
// determines the necessary size of the vtable and the number of statics that
// this class requires. The necessary memory is then allocated for a EEClass
// and its vtable and statics. The class members are then initialized and
// the memory is then returned to the caller
//
// LPEEClass CreateClass()
//
// Parameters :
// [in] scope - scope of the current class not the one requested to be opened
// [in] cl - class token of the class to be created.
// [out] ppEEClass - pointer to pointer to hold the address of the EEClass
// allocated in this function.
// Return : returns an HRESULT indicating the success of this function.
//
// This parameter has been removed but might need to be reinstated if the
// global for the metadata loader is removed.
// [in] pIMLoad - MetaDataLoader class/object for the current scope.
//==========================================================================
/*static*/
void MethodTableBuilder::CreateClass(BaseDomain * pDomain,
Module *pModule,
mdTypeDef cl,
BOOL fHasLayout,
BOOL fDelegate,
BOOL fIsEnum,
const MethodTableBuilder::bmtGenericsInfo *bmtGenericsInfo,
EEClass **ppEEClass,
size_t *pdwAllocRequestSize,
AllocMemTracker *pamTracker)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
PRECONDITION(!(fHasLayout && fDelegate));
PRECONDITION(!(fHasLayout && fIsEnum));
PRECONDITION(ppEEClass!=NULL);
PRECONDITION(pdwAllocRequestSize!=NULL);
PRECONDITION(CheckPointer(bmtGenericsInfo));
}
CONTRACTL_END;
EEClass *pEEClass = NULL;
IMDInternalImport *pInternalImport;
HRESULT hrToThrow;
pEEClass = NULL;
if (fHasLayout)
{
pEEClass = new (0,pDomain->GetLowFrequencyHeap(), pModule, pdwAllocRequestSize, pamTracker) LayoutEEClass(pModule, bmtGenericsInfo->genericsKind);
}
else if (fDelegate)
{
pEEClass = new (0,pDomain->GetLowFrequencyHeap(), pModule, pdwAllocRequestSize, pamTracker) DelegateEEClass(pModule, bmtGenericsInfo->genericsKind);
}
else if (fIsEnum)
{
pEEClass = new (0,pDomain->GetLowFrequencyHeap(), pModule, pdwAllocRequestSize, pamTracker) EnumEEClass(pModule, bmtGenericsInfo->genericsKind);
}
else
{
pEEClass = new (0,pDomain->GetLowFrequencyHeap(), pModule, pdwAllocRequestSize, pamTracker) EEClass(pModule, bmtGenericsInfo->genericsKind);
}
if (!pEEClass)
{
COMPlusThrowOM();
}
// Caller will clean up if we throw below here.
*ppEEClass = pEEClass;
DWORD dwAttrClass = 0;
mdToken tkExtends = mdTokenNil;
pEEClass->m_cl = cl;
// Set up variance info
if (bmtGenericsInfo->pVarianceInfo)
{
pEEClass->m_pVarianceInfo = (BYTE*) pamTracker->Track(pDomain->GetHighFrequencyHeap()->AllocMem(bmtGenericsInfo->GetNumGenericArgs()));
memcpy(pEEClass->m_pVarianceInfo, bmtGenericsInfo->pVarianceInfo, bmtGenericsInfo->GetNumGenericArgs());
}
else
pEEClass->m_pVarianceInfo = NULL;
pInternalImport = pModule->GetMDImport();
if (pInternalImport == NULL)
COMPlusThrowHR(COR_E_TYPELOAD);
pInternalImport->GetTypeDefProps(
cl,
&dwAttrClass,
&tkExtends
);
pEEClass->m_dwAttrClass = dwAttrClass;
// MDVal check: can't be both tdSequentialLayout and tdExplicitLayout
if((dwAttrClass & tdLayoutMask) == tdLayoutMask)
COMPlusThrowHR(COR_E_TYPELOAD);
if (IsTdInterface(dwAttrClass))
{
// MDVal check: must have nil tkExtends and must be tdAbstract
if((tkExtends & 0x00FFFFFF)||(!IsTdAbstract(dwAttrClass)))
COMPlusThrowHR(COR_E_TYPELOAD);
}
//
// Initialize SecurityProperties structure
//
if (Security::IsSecurityOn() && IsTdHasSecurity(dwAttrClass))
{
DWORD dwSecFlags;
DWORD dwNullDeclFlags;
hrToThrow = Security::GetDeclarationFlags(pInternalImport, cl, &dwSecFlags, &dwNullDeclFlags);
if (FAILED(hrToThrow))
COMPlusThrowHR(hrToThrow);
pEEClass->m_SecProps.SetFlags(dwSecFlags, dwNullDeclFlags);
}
// Cache class level reliability contract info.
pEEClass->SetReliabilityContract(::GetReliabilityContract(pInternalImport, cl));
if (fHasLayout)
pEEClass->SetHasLayout();
#ifdef _DEBUG
pModule->GetClassLoader()->m_dwDebugClasses++;
#endif
}
//*******************************************************************************
//
// Create a hash of all methods in this class. The hash is from method name to MethodDesc.
//
MethodNameHash *MethodTableBuilder::CreateMethodChainHash(MethodTable *pMT)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
Thread *pThread = GetThread();
PVOID pvMem = pThread->m_MarshalAlloc.Alloc(sizeof(MethodNameHash));
MethodNameHash *pHash = new (pvMem) 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;
}
//*******************************************************************************
//-----------------------------------------------------------------------------------
// Note: this only loads the type to CLASS_DEPENDENCIES_LOADED as this can be called
// indirectly from DoFullyLoad() as part of accessibility checking.
//-----------------------------------------------------------------------------------
MethodTable *MethodTable::LoadEnclosingMethodTable()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
MODE_ANY;
}
CONTRACTL_END
if (! GetClass()->IsNested())
return NULL;
mdTypeDef tdEnclosing = mdTypeDefNil;
HRESULT hr;
hr = GetMDImport()->GetNestedClassProps(GetCl(), &tdEnclosing);
if (FAILED(hr))
ThrowHR(hr, BFA_UNABLE_TO_GET_NESTED_PROPS);
return ClassLoader::LoadTypeDefThrowing(GetModule(),
tdEnclosing,
ClassLoader::ThrowIfNotFound,
ClassLoader::PermitUninstDefOrRef,
tdNoTypes,
CLASS_DEPENDENCIES_LOADED
).GetMethodTable();
}
//*******************************************************************************
//
// 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
{
THROWS;
GC_TRIGGERS;
PRECONDITION(CheckPointer(this));
PRECONDITION(CheckPointer(bmtParent));
PRECONDITION(CheckPointer(pModule));
PRECONDITION(CheckPointer(ppMethodDesc));
PRECONDITION(CheckPointer(ppMemberSignature));
PRECONDITION(CheckPointer(pcMemberSignature));
}
CONTRACTL_END;
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
bmtParent->pParentMethodHash = CreateMethodChainHash(bmtParent->pParentMethodTable);
}
// We have a hash table, so use it
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
*ppMemberSignature = pModule->GetMDImport()->GetSigOfMethodDef(
mdToken,
pcMemberSignature
);
// 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;
DWORD cHashMethodSig;
Substitution* pSubst = NULL;
MethodTable *entryMT = pEntry->m_pDesc->GetMethodTable();
EEClass *entryEEClass = entryMT->GetClass();
if (entryEEClass->GetNumGenericArgs() > 0)
{
EEClass *here = GetHalfBakedClass();
_ASSERTE(here->GetModule());
MethodTable *pParent = bmtParent->pParentMethodTable;
do
{
Substitution *newSubst = new Substitution;
*newSubst = here->GetSubstitutionForParent(pSubst);
pSubst = newSubst;
here = pParent->GetClass();
_ASSERT(here != NULL);
pParent = pParent->GetParentMethodTable();
}
while (entryEEClass != here);
}
// Get sig of entry in hash chain
pEntry->m_pDesc->GetSig(&pHashMethodSig, &cHashMethodSig);
// Note instantiation info
{
HRESULT hr = MetaSig::CompareMethodSigsNT(*ppMemberSignature, *pcMemberSignature, pModule, NULL,
pHashMethodSig, cHashMethodSig, pEntry->m_pDesc->GetModule(), pSubst);
if (FAILED(hr))
{
if(pSubst) pSubst->DeleteChain();
return hr;
}
if (hr == S_OK)
{
// Found a match
*ppMethodDesc = pEntry->m_pDesc;
// Check the constraints are consistent,
// and return the result to the caller.
// We do this here to avoid recalculating pSubst.
*pMethodConstraintsMatch =
MetaSig::CompareMethodConstraints(pModule, mdToken,
pSubst, pEntry->m_pDesc->GetModule(), pEntry->m_pDesc->GetMemberDef());
if(pSubst) pSubst->DeleteChain();
return S_OK;
}
}
if(pSubst) pSubst->DeleteChain();
// Advance to next item in the hash chain which has the same name
do
{
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;
}
//*******************************************************************************
// Enumerator to traverse the interface declarations of a type, automatically building
// a substitution chain on the stack.
class InterfaceImplEnum
{
Module* m_pModule;
HENUMInternalHolder hEnumInterfaceImpl;
const Substitution *m_pSubstChain;
Substitution m_CurrSubst;
mdTypeDef m_CurrTok;
public:
InterfaceImplEnum(Module *pModule, mdTypeDef cl, const Substitution *pSubstChain)
: hEnumInterfaceImpl(pModule->GetMDImport())
{
WRAPPER_CONTRACT;
m_pModule = pModule;
hEnumInterfaceImpl.EnumInit(mdtInterfaceImpl, cl);
m_pSubstChain = pSubstChain;
}
BOOL Next()
{
WRAPPER_CONTRACT;
mdInterfaceImpl ii;
if (!m_pModule->GetMDImport()->EnumNext(&hEnumInterfaceImpl, &ii))
return FALSE;
m_CurrTok = m_pModule->GetMDImport()->GetTypeOfInterfaceImpl(ii);
m_CurrSubst = Substitution(m_CurrTok, m_pModule, m_pSubstChain);
return TRUE;
}
const Substitution *CurrentSubst() const { LEAF_CONTRACT; return &m_CurrSubst; }
mdTypeDef CurrentToken() const { LEAF_CONTRACT; return m_CurrTok; }
};
//*******************************************************************************
//
// Given an interface map to fill out, expand pNewInterface (and its sub-interfaces) into it, increasing
// pdwInterfaceListSize as appropriate, and avoiding duplicates.
//
/* static */
void MethodTableBuilder::ExpandApproxInterface(bmtInterfaceInfo *bmtInterface,
const Substitution *pNewInterfaceSubstChain,
MethodTable *pNewInterface,
WORD flags)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END
// We expand the tree of inherited interfaces into a set by adding the
// current node BEFORE expanding the parents of the current node.
// ****** This must be consistent with ExpandExactInterface *******
// ****** This must be consistent with BuildInteropVTable_ExpandApproxInterface *******
// 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 (DWORD i = 0; i < bmtInterface->dwInterfaceMapSize; i++)
{
if (MetaSig::CompareTypeDefsUnderSubstitutions(bmtInterface->pInterfaceMap[i].m_pMethodTable,
pNewInterface,
bmtInterface->ppInterfaceSubstitutionChains[i],
pNewInterfaceSubstChain))
{
bmtInterface->pInterfaceMap[i].m_wFlags |= flags;
return; // found it, don't add it again
}
}
if (pNewInterface->GetNumVirtuals() > bmtInterface->dwLargestInterfaceSize)
bmtInterface->dwLargestInterfaceSize = pNewInterface->GetNumVirtuals();
DWORD n = bmtInterface->dwInterfaceMapSize;
// Add it and each sub-interface
// Save a copy of ths substitution chain for use later in loading and for subsequent comparisons
bmtInterface->pInterfaceMap[n].m_pMethodTable = pNewInterface;
bmtInterface->pInterfaceMap[n].SetInteropStartSlot(MethodTable::NO_SLOT);
bmtInterface->pInterfaceMap[n].m_wFlags = flags;
bmtInterface->ppInterfaceSubstitutionChains[n] = (Substitution *) GetThread()->m_MarshalAlloc.Alloc(sizeof(Substitution) * pNewInterfaceSubstChain->GetLength());
pNewInterfaceSubstChain->CopyToArray(bmtInterface->ppInterfaceSubstitutionChains[n]);
bmtInterface->dwInterfaceMapSize++;
ExpandApproxDeclaredInterfaces(bmtInterface,
pNewInterface->GetModule(),
pNewInterface->GetCl(),
pNewInterfaceSubstChain,
flags & ~InterfaceInfo_t::interface_declared_on_class);
}
//*******************************************************************************
/* static */
void MethodTableBuilder::ExpandApproxDeclaredInterfaces(bmtInterfaceInfo *bmtInterface,
Module *pModule,
mdToken typeDef,
const Substitution *pSubstChain,
WORD flags)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END
InterfaceImplEnum ie(pModule, typeDef, pSubstChain);
while (ie.Next())
{
MethodTable *pGenericIntf = ClassLoader::LoadApproxTypeThrowing(pModule, ie.CurrentToken(), NULL, NULL).GetMethodTable();
CONSISTENCY_CHECK(pGenericIntf->IsInterface());
ExpandApproxInterface(bmtInterface, ie.CurrentSubst(), pGenericIntf, flags);
}
}
//*******************************************************************************
/* static */
void MethodTableBuilder::ExpandApproxInherited(bmtInterfaceInfo *bmtInterface,
MethodTable *pApproxMT,
const Substitution *pSubstChain)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END
// Expand interfaces in superclasses first. Interfaces inherited from parents
// must have identical indexes as in the parent.
MethodTable *pParentOfParent = pApproxMT->GetParentMethodTable();
if (pParentOfParent)
{
Substitution parentSubst = pApproxMT->GetClass()->GetSubstitutionForParent(pSubstChain);
ExpandApproxInherited(bmtInterface,pParentOfParent,&parentSubst);
}
ExpandApproxDeclaredInterfaces(bmtInterface,
pApproxMT->GetModule(),pApproxMT->GetCl(),
pSubstChain,InterfaceInfo_t::interface_implemented_on_parent);
}
//*******************************************************************************
//
// 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.
//
void MethodTableBuilder::LoadApproxInterfaceMap(BuildingInterfaceInfo_t *pBuildingInterfaceList,
MethodTable *pApproxParentMT)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END
bmtInterface->dwInterfaceMapSize = 0;
// First inherit all the parent's interfaces. This is important, because our interface map must
// list the interfaces in identical order to our parent.
//
// <NICE> we should document the reasons why. One reason is that DispatchMapTypeIDs can be indexes
// into the list </NICE>
if (pApproxParentMT)
{
Substitution parentSubst = GetHalfBakedClass()->GetSubstitutionForParent(NULL);
MethodTableBuilder::ExpandApproxInherited(bmtInterface,
pApproxParentMT,
&parentSubst);
}
// Now add in any freshly declared interfaces, possibly augmenting the flags
MethodTableBuilder::ExpandApproxDeclaredInterfaces(bmtInterface,
GetModule(), GetCl(),
NULL,
InterfaceInfo_t::interface_declared_on_class);
}
//*******************************************************************************
DispatchMapTypeID MethodTableBuilder::ComputeDispatchMapTypeID(MethodTable *pDeclInftMT, const Substitution *pDeclIntfSubst)
{
WRAPPER_CONTRACT;
if (!pDeclInftMT->IsInterface())
{
return DispatchMapTypeID::ThisClassID();
}
else
{
for (DWORD idx = 0; idx < bmtInterface->dwInterfaceMapSize; idx++)
{
if (MetaSig::CompareTypeDefsUnderSubstitutions(bmtInterface->pInterfaceMap[idx].m_pMethodTable,
pDeclInftMT,
bmtInterface->ppInterfaceSubstitutionChains[idx],
pDeclIntfSubst)) {
return DispatchMapTypeID::InterfaceClassID(idx);
}
}
return DispatchMapTypeID::InterfaceNotImplementedID();
}
}
//*******************************************************************************
DispatchMapTypeID MethodTable::ComputeDispatchMapTypeID(MethodTable *pExactIntfMT, MethodTable *pExactImplMT)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
PRECONDITION(CheckPointer(pExactIntfMT));
PRECONDITION(CheckPointer(pExactImplMT));
} CONTRACTL_END;
if (!pExactIntfMT->IsInterface())
{
return DispatchMapTypeID::ThisClassID();
}
else
{
InterfaceMapIterator it = pExactImplMT->IterateInterfaceMap();
while (it.Next())
{
if (g_pConfig->ExactInterfaceCalls())
{
if (it.InterfaceEquals(pExactIntfMT))
{
return DispatchMapTypeID::InterfaceClassID(it.GetIndex());
}
}
else
{
if (it.GetInterface()->GetCanonicalMethodTable() == pExactIntfMT->GetCanonicalMethodTable())
{
return DispatchMapTypeID::InterfaceClassID(it.GetIndex());
}
}
}
return DispatchMapTypeID::InterfaceNotImplementedID();
}
}
//*******************************************************************************
/*static*/
VOID DECLSPEC_NORETURN MethodTableBuilder::BuildMethodTableThrowException(HRESULT hr,
const bmtErrorInfo & bmtError)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END
LPCUTF8 pszClassName, pszNameSpace;
bmtError.pModule->GetMDImport()->GetNameOfTypeDef(bmtError.cl, &pszClassName, &pszNameSpace);
if ((! bmtError.dMethodDefInError || bmtError.dMethodDefInError == mdMethodDefNil) &&
bmtError.szMethodNameForError == NULL) {
if (hr == E_OUTOFMEMORY)
COMPlusThrowOM();
else
bmtError.pModule->GetAssembly()->ThrowTypeLoadException(pszNameSpace, pszClassName,
bmtError.resIDWhy);
}
else {
LPCUTF8 szMethodName;
if(bmtError.szMethodNameForError == NULL)
szMethodName = (bmtError.pModule->GetMDImport())->GetNameOfMethodDef(bmtError.dMethodDefInError);
else
szMethodName = bmtError.szMethodNameForError;
bmtError.pModule->GetAssembly()->ThrowTypeLoadException(pszNameSpace, pszClassName,
szMethodName, bmtError.resIDWhy);
}
}
//*******************************************************************************
void MethodTableBuilder::SetBMTData(
BaseDomain *bmtDomain,
bmtErrorInfo *bmtError,
bmtProperties *bmtProp,
bmtVtable *bmtVT,
bmtParentInfo *bmtParent,
bmtInterfaceInfo *bmtInterface,
bmtMetaDataInfo *bmtMetaData,
bmtMethAndFieldDescs *bmtMFDescs,
bmtFieldPlacement *bmtFP,
bmtInternalInfo *bmtInternal,
bmtGCSeriesInfo *bmtGCSeries,
bmtMethodImplInfo *bmtMethodImpl,
const bmtGenericsInfo *bmtGenerics,
bmtEnumMethAndFields *bmtEnumMF,
bmtThreadContextStaticInfo *bmtTCSInfo)
{
LEAF_CONTRACT;
this->bmtDomain = bmtDomain;
this->bmtError = bmtError;
this->bmtProp = bmtProp;
this->bmtVT = bmtVT;
this->bmtParent = bmtParent;
this->bmtInterface = bmtInterface;
this->bmtMetaData = bmtMetaData;
this->bmtMFDescs = bmtMFDescs;
this->bmtFP = bmtFP;
this->bmtInternal = bmtInternal;
this->bmtGCSeries = bmtGCSeries;
this->bmtMethodImpl = bmtMethodImpl;
this->bmtGenerics = bmtGenerics;
this->bmtEnumMF = bmtEnumMF;
this->bmtTCSInfo = bmtTCSInfo;
}
//*******************************************************************************
void MethodTableBuilder::NullBMTData()
{
LEAF_CONTRACT;
this->bmtDomain = NULL;
this->bmtError = NULL;
this->bmtProp = NULL;
this->bmtVT = NULL;
this->bmtParent = NULL;
this->bmtInterface = NULL;
this->bmtMetaData = NULL;
this->bmtMFDescs = NULL;
this->bmtFP = NULL;
this->bmtInternal = NULL;
this->bmtGCSeries = NULL;
this->bmtMethodImpl = NULL;
this->bmtGenerics = NULL;
this->bmtEnumMF = NULL;
this->bmtTCSInfo = NULL;
}
//*******************************************************************************
//
// Builds the method table, allocates MethodDesc, handles overloaded members, attempts to compress
// interface storage. All dependent classes must already be resolved!
//
VOID MethodTableBuilder::BuildMethodTableThrowing(BaseDomain *bmtDomain,
Module *pLoaderModule,
Module *pModule,
mdToken cl,
BuildingInterfaceInfo_t *pBuildingInterfaceList,
const LayoutRawFieldInfo *pLayoutRawFieldInfos,
MethodTable *pParentMethodTable,
const bmtGenericsInfo *bmtGenericsInfo,
PCCOR_SIGNATURE parentInst,
WORD wNumInterfaces,
AllocMemTracker *pamTracker)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
PRECONDITION(CheckPointer(GetHalfBakedClass()));
PRECONDITION(CheckPointer(bmtGenericsInfo));
}
CONTRACTL_END;
// pThread is only used in certain scenarios. Avoid Rotor unused var error
Thread *pThread = GetThread();
pModule->EnsureLibraryLoaded();
// The following structs, defined as private members of MethodTableBuilder, contain the necessary local
// parameters needed for BuildMethodTable
// Look at the struct definitions for a detailed list of all parameters available
// to BuildMethodTable.
bmtErrorInfo bmtError;
bmtProperties bmtProp;
bmtVtable bmtVT;
bmtParentInfo bmtParent;
bmtInterfaceInfo bmtInterface;
bmtMetaDataInfo bmtMetaData;
bmtMethAndFieldDescs bmtMFDescs;
bmtFieldPlacement bmtFP;
bmtInternalInfo bmtInternal;
bmtGCSeriesInfo bmtGCSeries;
bmtMethodImplInfo bmtMethodImpl;
bmtThreadContextStaticInfo bmtTCSInfo;
this->bmtGenerics = bmtGenericsInfo;
//Initialize structs
bmtProp.fMarshaledByRef = false;
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 = GetCl();
bmtInterface.dwInterfaceMapSize = wNumInterfaces;
bmtInternal.pInternalImport = pModule->GetMDImport();
bmtInternal.pModule = pModule;
bmtInternal.cl = GetCl();
bmtEnumMethAndFields bmtEnumMF(bmtInternal.pInternalImport);
bmtParent.parentSubst = Substitution(pModule,parentInst,NULL);
DWORD dwAttrClass;
bmtInternal.pInternalImport->GetTypeDefProps(
bmtInternal.cl,
&dwAttrClass,
&(bmtParent.token)
);
SetBMTData(
bmtDomain,
&bmtError,
&bmtProp,
&bmtVT,
&bmtParent,
&bmtInterface,
&bmtMetaData,
&bmtMFDescs,
&bmtFP,
&bmtInternal,
&bmtGCSeries,
&bmtMethodImpl,
bmtGenericsInfo,
&bmtEnumMF,
&bmtTCSInfo);
// put the interior stack probe after all the stack-allocted goop above. We check compare our this pointer to the SP on
// the dtor to determine if we are being called on an EH path or not.
INTERIOR_STACK_PROBE_FOR(pThread, 8);
// If not NULL, it means there are some by-value fields, and this contains an entry for each instance or static field,
// which is NULL if not a by value field, and points to the EEClass of the field if a by value field. Instance fields
// come first, statics come second.
NewArrayHolder<EEClass*>pByValueClassCache(NULL);
// If not NULL, it means there are some by-value fields, and this contains an entry for each inst
#ifdef _DEBUG
LPCUTF8 className;
LPCUTF8 nameSpace;
bmtInternal.pInternalImport->GetNameOfTypeDef(bmtInternal.cl, &className, &nameSpace);
unsigned fileNameSize = 0;
LPCWSTR fileName = NULL;
// Personal choice: I do not want to see the full filename in every class name
// (i.e. System.IO.TextWriter[E:\WINDOWS\Microsoft.NET\Framework\v1.2.AMD64dbg\mscorlib.dll] )
if (pModule->IsPEFile()) {
fileName = pModule->GetFile()->GetDebugName();
if (fileName != 0)
fileNameSize = (unsigned int) wcslen(fileName) + 2;
}
{
size_t len = sizeof(char)*(strlen(className) + strlen(nameSpace) + fileNameSize + 2);
char *name = (char*) pamTracker->Track(bmtDomain->GetHighFrequencyHeap()->AllocMem(len));
strcpy_s(name, len, nameSpace);
if (strlen(nameSpace) > 0) {
name[strlen(nameSpace)] = '.';
name[strlen(nameSpace) + 1] = '\0';
}
strcat_s(name, len, className);
if (fileNameSize != 0 && g_pConfig->ShouldAppendFileNameToTypeName()) {
char* ptr = name + strlen(name);
*ptr++ = '[';
while(*fileName != 0)
*ptr++ = char(*fileName++);
*ptr++ = ']';
*ptr++ = 0;
}
GetHalfBakedClass()->SetDebugClassName(name);
}
if (g_pConfig->ShouldBreakOnClassBuild(className)) {
_ASSERTE(!"BreakOnClassBuild");
GetHalfBakedClass()->m_fDebuggingClass = TRUE;
}
#endif // _DEBUG
DWORD i;
#ifdef _DEBUG
LPCUTF8 pszDebugName,pszDebugNamespace;
pModule->GetMDImport()->GetNameOfTypeDef(bmtInternal.cl, &pszDebugName, &pszDebugNamespace);
#endif // _DEBUG
#ifdef _DEBUG
if (bmtGenerics->HasInstantiation())
{
StackSString debugName(SString::Utf8, GetDebugClassName());
TypeString::AppendInst(debugName, bmtGenerics->GetNumGenericArgs(), bmtGenerics->GetInstantiation(), TypeString::FormatBasic);
StackScratchBuffer buff;
const char* pDebugNameUTF8 = debugName.GetUTF8(buff);
size_t len = strlen(pDebugNameUTF8)+1;
char *name = (char*) pamTracker->Track(bmtDomain->GetLowFrequencyHeap()->AllocMem(len));
strcpy_s(name, len, pDebugNameUTF8);
GetHalfBakedClass()->SetDebugClassName(name);
}
#endif // _DEBUG
// If this is mscorlib, then don't perform some sanity checks on the layout
bmtProp.fNoSanityChecks = ((g_pObjectClass != NULL) && pModule == g_pObjectClass->GetModule());
#ifdef _DEBUG
StackSString debugName(SString::Utf8, pszDebugName);
if (bmtGenerics->HasInstantiation())
{
TypeString::AppendInst(debugName, bmtGenerics->GetNumGenericArgs(), bmtGenerics->GetInstantiation(), TypeString::FormatBasic);
}
LOG((LF_CLASSLOADER, LL_INFO1000, "Loading class \"%s%s%S\" from module \"%ws\" in domain 0x%x %s\n",
*pszDebugNamespace ? pszDebugNamespace : "",
*pszDebugNamespace ? NAMESPACE_SEPARATOR_STR : "",
debugName.GetUnicode(),
pModule->GetDebugName(),
pModule->GetDomain(),
(pModule->IsSystem()) ? "System Domain" : ""
));
#endif // _DEBUG
// 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 class
if (IsInterface())
{
pParentMethodTable = NULL;
}
unsigned totalDeclaredFieldSize=0;
bmtParent.pParentMethodTable = pParentMethodTable;
// Check to see if the class is an valuetype
if(pParentMethodTable != NULL
&& ((g_pEnumClass != NULL && pParentMethodTable == g_pValueTypeClass) ||
pParentMethodTable == g_pEnumClass))
{
SetValueClass();
HRESULT hr = bmtInternal.pInternalImport->GetCustomAttributeByName(bmtInternal.cl,
g_CompilerServicesUnsafeValueTypeAttribute,
NULL, NULL);
IfFailThrow(hr);
if (hr == S_OK)
{
SetUnsafeValueClass();
}
}
// Check to see if the class is an enumeration
if(pParentMethodTable != NULL && pParentMethodTable == g_pEnumClass)
{
SetEnum();
// Ensure we don't have generic enums, or at least enums that have a
// different number of type parameters from their enclosing class.
// The goal is to ensure that the enum's values can't depend on the
// type parameters in any way. And we don't see any need for an
// enum to have additional type parameters.
if (bmtGenerics->GetNumGenericArgs() != 0)
{
// Nested enums can have generic type parameters from their enclosing class.
// CLS rules require type parameters to be propogated to nested types.
// Note that class G<T> { enum E { } } will produce "G`1+E<T>".
// We want to disallow class G<T> { enum E<T, U> { } }
// Perhaps the IL equivalent of class G<T> { enum E { } } should be legal.
if (!IsNested())
BuildMethodTableThrowException(IDS_CLASSLOAD_ENUM_EXTRA_GENERIC_TYPE_PARAM);
bool fWrongNumberOfTypeParams = false;
mdTypeDef tdEnclosing = mdTypeDefNil;
HRESULT hr = bmtInternal.pInternalImport->GetNestedClassProps(GetCl(), &tdEnclosing);
if (FAILED(hr))
ThrowHR(hr, BFA_UNABLE_TO_GET_NESTED_PROPS);
HENUMInternal hEnumGenericPars;
if (FAILED(bmtInternal.pInternalImport->EnumInit(mdtGenericParam, tdEnclosing, &hEnumGenericPars)))
GetAssembly()->ThrowTypeLoadException(bmtInternal.pInternalImport, tdEnclosing, IDS_CLASSLOAD_BADFORMAT);
// Iterate past end of generic type params
for (unsigned j = 0; j < bmtGenerics->GetNumGenericArgs() + 1; j++)
{
mdGenericParam tkTyPar = mdGenericParamNil;
bmtInternal.pInternalImport->EnumNext(&hEnumGenericPars, &tkTyPar);
// Check if we got back null?
if (IsNilToken(tkTyPar)) {
fWrongNumberOfTypeParams = (j < bmtGenerics->GetNumGenericArgs());
break;
}
/*
else if (j == bmtGenerics->GetNumGenericArgs()) {
fWrongNumberOfTypeParams = true;
}
*/
}
bmtInternal.pInternalImport->EnumClose(&hEnumGenericPars);
if (fWrongNumberOfTypeParams)
BuildMethodTableThrowException(IDS_CLASSLOAD_ENUM_EXTRA_GENERIC_TYPE_PARAM);
}
}
bmtParent.pParentMethodTable = pParentMethodTable;
if (bmtParent.pParentMethodTable)
{
}
else if (! (IsInterface() ) )
{
if(g_pObjectClass != NULL)
{
if(g_pObjectClass->GetAssembly()->GetManifestFile()->Equals(GetAssembly()->GetManifestFile()) &&
!IsGlobalClass())
{
BuildMethodTableThrowException(IDS_CLASSLOAD_PARENTNULL);
}
}
}
// Set the contextful or marshalbyref flag if necessary
SetContextfulOrByRef();
// NOTE: This appears to be the earliest point during class loading that other classes MUST be loaded
// resolve unresolved interfaces, determine an upper bound on the size of the interface map,
// and determine the size of the largest interface (in # slots)
ResolveInterfaces(pBuildingInterfaceList);
// Compute parent class and interface module dependencies
ComputeModuleDependencies();
// Enumerate this class's members
EnumerateMethodImpls();
// Enumerate this class's members
EnumerateClassMembers();
// This will allocate the working versions of the VTable and NonVTable in bmtVT
AllocateWorkingSlotTables();
// Allocate a MethodDesc* for each method (needed later when doing interfaces), and a FieldDesc* for each field
AllocateMethodFieldDescs(pamTracker);
// Go thru all fields and initialize their FieldDescs.
InitializeFieldDescs(bmtDomain, GetApproxFieldDescListRaw(), pLayoutRawFieldInfos, &bmtInternal, bmtGenerics,
&bmtMetaData, &bmtEnumMF, &bmtError,
&pByValueClassCache, &bmtMFDescs, &bmtFP, &bmtTCSInfo,
&totalDeclaredFieldSize, &bmtParent);
void *pv = pThread->m_MarshalAlloc.Alloc(sizeof(DispatchMapBuilder));
bmtVT.pDispatchMapBuilder = new (pv) DispatchMapBuilder(&pThread->m_MarshalAlloc);
// Determine vtable placement for each member in this class
PlaceMembers((DWORD) wNumInterfaces, pBuildingInterfaceList, pamTracker);
//
// 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())
{
PlaceVtableMethods((DWORD) wNumInterfaces, pBuildingInterfaceList);
PlaceMethodImpls(pamTracker);
// Now that interface method implementation have been fully resolved,
// we need to make sure that type constraints are also met.
ValidateInterfaceMethodConstraints();
}
// If we're a value class, we want to create duplicate slots
// and MethodDescs for all methods in the vtable
// section (i.e. not non-virtual instance methods or statics).
//
// This does not do non-virtual instance methods, which means
// we have no BoxedEntryPointStubs available for these in the methodtable.
// These are stored in the AssociatedMethodHash (see FindOrCreateAssociatedMethod).
//
// if (!bmtProp.fContainsGenericVariables)
ChangeValueClassVirtualsToBoxedEntryPointsAndCreateUnboxedEntryPoints(pamTracker);
// Verify that we have not overflowed the number of slots.
if (!FitsInU2((UINT64)bmtVT.dwCurrentVtableSlot + (UINT64)bmtVT.dwCurrentNonVtableSlot))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_TOO_MANY_METHODS);
}
// Place all non vtable methods
for (i = 0; i < bmtVT.dwCurrentNonVtableSlot; i++)
{
MethodDesc *pMD = bmtVT.pNonVtableMD[i];
_ASSERTE(pMD->GetSlot() == i);
pMD->SetSlot(pMD->GetSlot() + (WORD) bmtVT.dwCurrentVtableSlot);
bmtVT.SetMethodDescForSlot(pMD->GetSlot(), pMD);
}
if (bmtVT.wDefaultCtorSlot != MethodTable::NO_SLOT)
bmtVT.wDefaultCtorSlot += (WORD) bmtVT.dwCurrentVtableSlot;
if (bmtVT.wCCtorSlot != MethodTable::NO_SLOT)
bmtVT.wCCtorSlot += (WORD) bmtVT.dwCurrentVtableSlot;
bmtVT.dwCurrentNonVtableSlot += bmtVT.dwCurrentVtableSlot;
// ensure we didn't overflow the temporary vtable
_ASSERTE(bmtVT.dwCurrentNonVtableSlot <= bmtVT.dwMaxVtableSize);
// Allocate dictionary layout used by all compatible instantiations
GetHalfBakedClass()->m_pDictLayout = NULL;
if (IsSharedByGenericInstantiations() && !bmtGenerics->fContainsGenericVariables)
{
// We use the number of methods as a heuristic for the number of slots in the dictionary
// attached to shared class method tables.
// If there are no declared methods then we have no slots, and we will never do any token lookups
//
// Heuristics
// - Classes with a small number of methods (2-3) tend to be more likely to use new slots,
// i.e. further methods tend to reuse slots from previous methods.
// = treat all classes with only 2-3 methods as if they have an extra method.
// - Classes with more generic parameters tend to use more slots.
// = multiply by 1.5 for 2 params or more
DWORD numMethodsAdjusted =
(bmtEnumMF.dwNumDeclaredNonAbstractMethods == 0)
? 0
: (bmtEnumMF.dwNumDeclaredNonAbstractMethods < 3)
? 3
: bmtEnumMF.dwNumDeclaredNonAbstractMethods;
_ASSERTE(bmtGenerics->GetNumGenericArgs() != 0);
DWORD nTypeFactorBy2 = (bmtGenerics->GetNumGenericArgs() == 1) ? 2 : 3;
DWORD estNumTypeSlots = (numMethodsAdjusted * nTypeFactorBy2 + 2) / 3;
DWORD numTypeSlots = estNumTypeSlots;
if (numTypeSlots > 0)
GetHalfBakedClass()->m_pDictLayout = DictionaryLayout::Allocate(numTypeSlots, bmtDomain);
}
// We decide here if we need a dynamic entry for our statics. We need it here because
// the offsets of our fields will depend on this. For the dynamic case (which requires
// an extra indirection (indirect depending of methodtable) we'll allocate the slot
// in setupmethodtable
if (((pModule->IsReflection() || bmtGenerics->HasInstantiation()) &&
(bmtVT.wCCtorSlot != MethodTable::NO_SLOT || bmtEnumMF.dwNumStaticFields !=0))
)
{
// We will need a dynamic id
bmtProp.fDynamicStatics = TRUE;
if (bmtGenerics->HasInstantiation())
{
bmtProp.fGenericsStatics = TRUE;
}
}
else
{
SetModuleDynamicID(MODULE_NON_DYNAMIC_STATICS);
}
// Place static fields
PlaceStaticFields();
#if _DEBUG
if (GetNumStaticFields() > 0)
{
LOG((LF_CODESHARING,
LL_INFO10000,
"Placing %d %sshared statics (%d handles) for class %s.\n",
GetNumStaticFields(), pModule->IsCompiledDomainNeutral() ? "" : "un", GetNumHandleStatics(),
pszDebugName));
}
#endif // _DEBUG
//#define NumStaticFieldsOfSize $$$$$
//#define StaticFieldStart $$$$$
if (IsBlittable())
{
SetNumGCPointerSeries(0);
bmtFP.NumInstanceGCPointerFields = 0;
_ASSERTE(HasLayout());
SetNumInstanceFieldBytes(((LayoutEEClass*)GetHalfBakedClass())->GetLayoutInfo()->m_cbNativeSize);
}
else if (IsManagedSequential())
{
SetNumGCPointerSeries(0);
bmtFP.NumInstanceGCPointerFields = 0;
_ASSERTE(HasLayout());
SetNumInstanceFieldBytes(((LayoutEEClass*)GetHalfBakedClass())->GetLayoutInfo()->m_cbManagedSize);
}
else
{
_ASSERTE(!IsBlittable());
// HandleExplicitLayout fails for the GenericTypeDefinition when
// it will succeed for some particular instantiations.
// Thus we only do explicit layout for real instantiations, e.g. C<int>, not
// the open types such as the GenericTypeDefinition C<!0> or any
// of the "fake" types involving generic type variables which are
// used for reflection and verification, e.g. C<List<!0>>.
if (!bmtGenerics->fContainsGenericVariables && HasExplicitFieldOffsetLayout())
{
HandleExplicitLayout(pByValueClassCache);
}
else
{
// Place instance fields
PlaceInstanceFields(pByValueClassCache);
}
}
// We enforce that all value classes have non-zero size
if (IsValueClass() && GetNumInstanceFieldBytes() == 0)
{
BuildMethodTableThrowException(IDS_CLASSLOAD_ZEROSIZE);
}
// If the class is serializable we scan it for VTS (Version Tolerant
// Serialization) event methods or NotSerialized or OptionalField
// fields. Any such info found will be attached to the method as
// optional data later.
if (IsTdSerializable(GetAttrClass()))
ScanTypeForVtsInfo();
// Now setup the method table
SetupMethodTable2(pamTracker, pLoaderModule);
MethodTable *pMT = GetHalfBakedMethodTable();
if (bmtGenerics->pVarianceInfo != NULL)
{
pMT->SetHasVariance();
}
if (bmtFP.fHasFixedAddressValueTypes)
{
// To make things simpler, if the class has any field with this requirement, we'll set
// all the statics to have this property. This allows us to only need to persist one bit
// for the ngen case.
pMT->SetFixedAddressStaticVTs();
}
if (IsValueClass() && (GetNumInstanceFieldBytes() != totalDeclaredFieldSize || HasOverLayedField()))
{
pMT->SetNotTightlyPacked();
}
#ifdef _DEBUG
pMT->SetDebugClassName(GetDebugClassName());
#endif // _DEBUG
if (HasExplicitFieldOffsetLayout())
// Perform relevant GC calculations for tdexplicit
HandleGCForExplicitLayout();
else
// Perform relevant GC calculations for value classes
HandleGCForValueClasses(pByValueClassCache);
if (pMT->ContainsPointers())
{
CGCDesc* gcDesc = CGCDesc::GetCGCDescFromMT(pMT);
qsort(gcDesc->GetLowestSeries(), (int)gcDesc->GetNumSeries(), sizeof(CGCDescSeries), compareCGCDescSeries);
}
if (MethodTable::ComputeIsPreInit(bmtGenerics->fContainsGenericVariables,
pMT->HasClassConstructor(),
(bmtFP.NumStaticGCBoxedFields > 0),
bmtProp.fDynamicStatics))
{
// Mark the class as needing no static initialization, i.e. we've done all necessary
// initialization at load time.
pMT->SetClassPreInited();
}
pMT->MaybeSetHasFinalizer();
#if CHECK_APP_DOMAIN_LEAKS
// Figure out if we're domain agile..
// Note that this checks a bunch of field directly on the class & method table,
// so it needs to come late in the game.
SetAppDomainAgileAttribute();
#endif // CHECK_APP_DOMAIN_LEAKS
// Allocate dynamic slot if necessary
if (bmtProp.fDynamicStatics)
{
if (bmtProp.fGenericsStatics)
{
FieldDesc* pStaticFieldDescs = NULL;
if (GetNumStaticFields() != 0)
{
pStaticFieldDescs = pMT->GetApproxFieldDescListRaw() + pMT->GetNumIntroducedInstanceFields();
}
pMT->SetupGenericsStaticsInfo(pStaticFieldDescs);
}
else
{
// Get an id for the dynamic class. We store it in the class because
// no class that is persisted in ngen should have it (ie, if the class is ngened
SetModuleDynamicID(GetModule()->AllocateDynamicEntry(pMT));
}
}
//
// if there are context or thread static set the info in the method table optional members
//
if (bmtTCSInfo.dwThreadStaticsSize != 0 || bmtTCSInfo.dwContextStaticsSize != 0)
{
if ((bmtTCSInfo.dwThreadStaticsSize != (WORD)bmtTCSInfo.dwThreadStaticsSize) ||
(bmtTCSInfo.dwContextStaticsSize != (WORD)bmtTCSInfo.dwContextStaticsSize)) {
BuildMethodTableThrowException(IDS_EE_TOOMANYFIELDS);
}
// this is responsible for setting the flag and allocation in the loader heap
pMT->SetupThreadOrContextStatics(pamTracker, (WORD)bmtTCSInfo.dwThreadStaticsSize, (WORD)bmtTCSInfo.dwContextStaticsSize);
}
// If we have a non-interface class, then do inheritance security
// checks on it. The check starts by checking for inheritance
// permission demands on the current class. If these first checks
// succeeded, then the cached declared method list is scanned for
// methods that have inheritance permission demands.
VerifyInheritanceSecurity();
// Check for the RemotingProxy Attribute
if (IsContextful())
{
_ASSERTE(g_pObjectClass);
// Skip mscorlib marshal-by-ref classes since they all
// are assumed to have the default proxy attribute
if (!(pModule == g_pObjectClass->GetModule()))
{
CheckForRemotingProxyAttrib();
}
}
if (IsContextful() || HasRemotingProxyAttribute())
{
// Contextful and classes that have a remoting proxy attribute
// (whether they are MarshalByRef or ContextFul) always take the slow
// path of managed activation
pMT->SetRequiresManagedActivation();
}
// structs with GC poitners MUST be pointer sized aligned because the GC assumes it
if (IsValueClass() && pMT->ContainsPointers() && GetNumInstanceFieldBytes() % sizeof(void*) != 0)
{
BuildMethodTableThrowException(IDS_CLASSLOAD_BADFORMAT);
}
if (IsInterface())
{
// Reset parent class
pMT->SetParentMethodTable (g_pObjectClass);
}
#ifdef _DEBUG
// Reset the debug method names for BoxedEntryPointStubs
// so they reflect the very best debug information for the methods
{
DeclaredMethodIterator methIt(*this);
while (methIt.Next())
{
if (methIt.GetUnboxedMethodDesc() != NULL)
{
{
MethodDesc *pMD = methIt.GetUnboxedMethodDesc();
StackSString name(SString::Utf8);
TypeString::AppendMethodDebug(name, pMD);
StackScratchBuffer buff;
const char* pDebugNameUTF8 = name.GetUTF8(buff);
size_t len = strlen(pDebugNameUTF8)+1;
pMD->m_pszDebugMethodName = (char*) pamTracker->Track(GetDomain()->GetLowFrequencyHeap()->AllocMem(len));
_ASSERTE(pMD->m_pszDebugMethodName);
strcpy_s((char *) pMD->m_pszDebugMethodName, len, pDebugNameUTF8);
}
{
MethodDesc *pMD = methIt.GetMethodDesc();
StackSString name(SString::Utf8);
TypeString::AppendMethodDebug(name, pMD);
StackScratchBuffer buff;
const char* pDebugNameUTF8 = name.GetUTF8(buff);
size_t len = strlen(pDebugNameUTF8)+1;
pMD->m_pszDebugMethodName = (char*) pamTracker->Track(GetDomain()->GetLowFrequencyHeap()->AllocMem(len));
_ASSERTE(pMD->m_pszDebugMethodName);
strcpy_s((char *) pMD->m_pszDebugMethodName, len, pDebugNameUTF8);
}
}
}
}
#endif // _DEBUG
// Make sure the object cloner won't attempt to blit types that aren't serializable.
if (!IsTdSerializable(GetAttrClass()) && !IsEnum())
SetCannotBeBlittedByObjectCloner();
//If this is a value type, then propagate the UnsafeValueTypeAttribute from
//its instance members to this type.
if (IsValueClass() && !IsUnsafeValueClass())
{
ApproxFieldDescIterator fields(GetHalfBakedMethodTable(),
ApproxFieldDescIterator::INSTANCE_FIELDS,
FALSE);
FieldDesc * current;
while (NULL != (current = fields.Next()))
{
CONSISTENCY_CHECK(!current->IsStatic());
if (current->GetFieldType() == ELEMENT_TYPE_VALUETYPE)
{
TypeHandle th = current->LookupApproxFieldTypeHandle();
CONSISTENCY_CHECK(!th.IsNull());
if (th.AsMethodTable()->GetClass()->IsUnsafeValueClass())
{
SetUnsafeValueClass();
break;
}
}
}
}
// Grow the typedef ridmap in advance as we can't afford to
// fail once we set the resolve bit
pModule->EnsureTypeDefCanBeStored(bmtInternal.cl);
// Grow the tables in advance so that RID map filling cannot fail
// once we're past the commit point.
EnsureRIDMapsCanBeFilled();
{
// NOTE. NOTE!! the EEclass can now be accessed by other threads.
// Do NOT place any initialization after this point.
// You may NOT fail the call after this point.
FAULT_FORBID();
CANNOTTHROWCOMPLUSEXCEPTION();
/*
pamTracker->SuppressRelease();
*/
}
#ifdef _DEBUG
if (g_pConfig->ShouldDumpOnClassLoad(pszDebugName))
{
LOG((LF_CLASSLOADER, LL_ALWAYS, "Method table summary for '%s':\n", pszDebugName));
LOG((LF_CLASSLOADER, LL_ALWAYS, "Number of static fields: %d\n", bmtEnumMF.dwNumStaticFields));
LOG((LF_CLASSLOADER, LL_ALWAYS, "Number of instance fields: %d\n", bmtEnumMF.dwNumInstanceFields));
LOG((LF_CLASSLOADER, LL_ALWAYS, "Number of static obj ref fields: %d\n", bmtEnumMF.dwNumStaticObjRefFields));
LOG((LF_CLASSLOADER, LL_ALWAYS, "Number of static boxed fields: %d\n", bmtEnumMF.dwNumStaticBoxedFields));
LOG((LF_CLASSLOADER, LL_ALWAYS, "Number of declared fields: %d\n", NumDeclaredFields()));
LOG((LF_CLASSLOADER, LL_ALWAYS, "Number of declared methods: %d\n", NumDeclaredMethods()));
LOG((LF_CLASSLOADER, LL_ALWAYS, "Number of declared non-abstract methods: %d\n", bmtEnumMF.dwNumDeclaredNonAbstractMethods));
pMT->DebugDumpVtable(pszDebugName, false);
GetHalfBakedClass()->DebugDumpFieldLayout(pszDebugName, false);
GetHalfBakedClass()->DebugDumpGCDesc(pszDebugName, false);
}
#endif // _DEBUG
STRESS_LOG3(LF_CLASSLOADER, LL_INFO1000, "MethodTableBuilder: finished method table for module %p token %x = %pT \n",
pModule,
GetCl(),
GetHalfBakedMethodTable());
// Make sure that nobody access what were stack-allocated structures in this method.
NullBMTData();
END_INTERIOR_STACK_PROBE;
}
//*******************************************************************************
// 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::ResolveInterfaces(BuildingInterfaceInfo_t *pBuildingInterfaceList)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM());
PRECONDITION(CheckPointer(this));
PRECONDITION(CheckPointer(bmtDomain));
PRECONDITION(CheckPointer(bmtInterface));
PRECONDITION(CheckPointer(bmtVT));
PRECONDITION(CheckPointer(bmtParent));
}
CONTRACTL_END;
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->dwInterfaceMapSize; i++)
{
MethodTable *pInterface = pBuildingInterfaceList[i].m_pMethodTable;
if (IsSerializerRelatedInterface(pInterface))
SetCannotBeBlittedByObjectCloner();
bmtInterface->dwMaxExpandedInterfaces += (1+ pInterface->GetNumInterfaces());
}
// Now look at interfaces inherited from the parent
if (bmtParent->pParentMethodTable != NULL)
{
//@GENERICS: for our purposes here we use the generic parent because all its interfaces will be preloaded
MethodTable::InterfaceMapIterator it = bmtParent->pParentMethodTable->IterateInterfaceMap();
while (it.Next())
{
MethodTable *pMT = it.GetInterface();
bmtInterface->dwMaxExpandedInterfaces += (1+pMT->GetNumInterfaces());
}
}
bmtInterface->ppInterfaceSubstitutionChains = (Substitution**) pThread->m_MarshalAlloc.Alloc(sizeof(Substitution *) * bmtInterface->dwMaxExpandedInterfaces);
// Create a fully expanded map of all interfaces we implement
bmtInterface->pInterfaceMap = (InterfaceInfo_t *) pThread->m_MarshalAlloc.Alloc(sizeof(InterfaceInfo_t) * bmtInterface->dwMaxExpandedInterfaces);
// # slots of largest interface
bmtInterface->dwLargestInterfaceSize = 0;
LoadApproxInterfaceMap(pBuildingInterfaceList,
bmtParent->pParentMethodTable);
_ASSERTE(bmtInterface->dwInterfaceMapSize <= 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 = (MethodDesc**)
pThread->m_MarshalAlloc.Alloc(bmtInterface->dwLargestInterfaceSize * sizeof(MethodDesc*));
bmtInterface->ppInterfaceDeclMethodDescList = (MethodDesc**)
pThread->m_MarshalAlloc.Alloc(bmtInterface->dwLargestInterfaceSize * sizeof(MethodDesc*));
}
MethodTable *pParentClass = (IsInterface() || bmtParent->pParentMethodTable == NULL) ? NULL : bmtParent->pParentMethodTable;
// For all the new interfaces we bring in, sum the methods
bmtInterface->dwTotalNewInterfaceMethods = 0;
if (pParentClass != NULL)
{
for (i = bmtParent->pParentMethodTable->GetNumInterfaces(); i < (bmtInterface->dwInterfaceMapSize); i++)
bmtInterface->dwTotalNewInterfaceMethods +=
bmtInterface->pInterfaceMap[i].m_pMethodTable->GetNumVirtuals();
}
// Inherit parental slot counts
if (pParentClass != NULL)
{
bmtVT->dwCurrentVtableSlot = bmtParent->pParentMethodTable->GetNumVirtuals();
bmtParent->dwNumParentInterfaces = bmtParent->pParentMethodTable->GetNumInterfaces();
bmtParent->NumParentPointerSeries = pParentClass->GetClass()->m_wNumGCPointerSeries;
if (pParentClass->HasFieldsWhichMustBeInited())
SetHasFieldsWhichMustBeInited();
if (pParentClass->CannotBeBlittedByObjectCloner())
SetCannotBeBlittedByObjectCloner();
}
else
{
bmtVT->dwCurrentVtableSlot = 0;
bmtParent->dwNumParentInterfaces = 0;
bmtParent->NumParentPointerSeries = 0;
}
bmtVT->dwCurrentNonVtableSlot = 0;
bmtInterface->pppInterfaceImplementingMD = (MethodDesc ***) pThread->m_MarshalAlloc.Alloc(sizeof(MethodDesc *) * bmtInterface->dwMaxExpandedInterfaces);
memset(bmtInterface->pppInterfaceImplementingMD, 0, sizeof(MethodDesc *) * bmtInterface->dwMaxExpandedInterfaces);
bmtInterface->pppInterfaceDeclaringMD = (MethodDesc ***) pThread->m_MarshalAlloc.Alloc(sizeof(MethodDesc *) * bmtInterface->dwMaxExpandedInterfaces);
memset(bmtInterface->pppInterfaceDeclaringMD, 0, sizeof(MethodDesc *) * bmtInterface->dwMaxExpandedInterfaces);
}
//*******************************************************************************
VOID MethodTableBuilder::ComputeModuleDependencies()
{
// Add dependencies for parents up the chain
MethodTable *pParent = bmtParent->pParentMethodTable;
while (pParent != NULL)
{
Module *pParentModule = pParent->GetModule();
if (pParentModule != bmtInternal->pModule)
bmtInternal->pModule->AddClassDependency(pParentModule, &GetHalfBakedClass()->m_classDependencies);
pParent = pParent->GetParentMethodTable();
}
}
//*******************************************************************************
/* static */
int __cdecl MethodTableBuilder::bmtMetaDataInfo::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::bmtMetaDataInfo::MethodImplTokenPair::Equal(
const MethodImplTokenPair *elem1,
const MethodImplTokenPair *elem2)
{
STATIC_CONTRACT_LEAF;
return ((elem1->methodBody == elem2->methodBody) &&
(elem1->methodDecl == elem2->methodDecl));
}
//*******************************************************************************
VOID MethodTableBuilder::EnumerateMethodImpls()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
MODE_ANY;
}
CONTRACTL_END
HRESULT hr = S_OK;
IMDInternalImport *pMDInternalImport = bmtInternal->pInternalImport;
DWORD rid, maxRidMD, maxRidMR;
hr = pMDInternalImport->EnumMethodImplInit(GetCl(),
&(bmtEnumMF->hEnumBody),
&(bmtEnumMF->hEnumDecl));
if (FAILED(hr))
{
BuildMethodTableThrowException(hr, *bmtError);
}
// We have successfully opened the enum, make sure to close when we're done.
bmtEnumMF->fNeedToCloseEnumMethodImpl = true;
// This gets the count out of the metadata interface.
bmtEnumMF->dwNumberMethodImpls = pMDInternalImport->EnumMethodImplGetCount(&(bmtEnumMF->hEnumBody), &(bmtEnumMF->hEnumDecl));
// 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 (bmtEnumMF->dwNumberMethodImpls > 0)
{
//
// Allocate the structures to keep track of the token pairs
//
DWORD cbAllocSize = 0;
if (!ClrSafeInt<DWORD>::multiply(bmtEnumMF->dwNumberMethodImpls, sizeof(bmtMetaDataInfo::MethodImplTokenPair), cbAllocSize))
ThrowHR(COR_E_OVERFLOW);
bmtMetaData->rgMethodImplTokens = (bmtMetaDataInfo::MethodImplTokenPair *)
GetThread()->m_MarshalAlloc.Alloc(cbAllocSize);
// Iterate through each MethodImpl declared on this class
for (DWORD i = 0; i < bmtEnumMF->dwNumberMethodImpls; i++)
{
// Grab the next set of body/decl tokens
if(!pMDInternalImport->EnumMethodImplNext(&(bmtEnumMF->hEnumBody),
&(bmtEnumMF->hEnumDecl),
&bmtMetaData->rgMethodImplTokens[i].methodBody,
&bmtMetaData->rgMethodImplTokens[i].methodDecl))
{
// 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?)
bmtEnumMF->dwNumberMethodImpls = i;
break;
}
}
// No need to do any sorting or duplicate elimination if there's not two or more methodImpls
if (bmtEnumMF->dwNumberMethodImpls > 1)
{
// Now sort
qsort(bmtMetaData->rgMethodImplTokens,
bmtEnumMF->dwNumberMethodImpls,
sizeof(bmtMetaDataInfo::MethodImplTokenPair),
&bmtMetaDataInfo::MethodImplTokenPair::Compare);
// Now eliminate duplicates
for (DWORD i = 0; i < bmtEnumMF->dwNumberMethodImpls - 1; i++)
{
CONSISTENCY_CHECK((i + 1) < bmtEnumMF->dwNumberMethodImpls);
bmtMetaDataInfo::MethodImplTokenPair *e1 = &bmtMetaData->rgMethodImplTokens[i];
bmtMetaDataInfo::MethodImplTokenPair *e2 = &bmtMetaData->rgMethodImplTokens[i + 1];
// If the pair are equal, eliminate the first one, and reduce the total count by one.
if (bmtMetaDataInfo::MethodImplTokenPair::Equal(e1, e2))
{
DWORD dwCopyNum = bmtEnumMF->dwNumberMethodImpls - (i + 1);
memcpy(e1, e2, dwCopyNum * sizeof(bmtMetaDataInfo::MethodImplTokenPair));
bmtEnumMF->dwNumberMethodImpls--;
CONSISTENCY_CHECK(bmtEnumMF->dwNumberMethodImpls > 0);
}
}
}
}
if(bmtEnumMF->dwNumberMethodImpls)
{
//
// Allocate the structures to keep track of the impl matches
//
bmtMetaData->pMethodDeclSubsts = (Substitution*) GetThread()->m_MarshalAlloc.Alloc(
bmtEnumMF->dwNumberMethodImpls * sizeof(Substitution));
bmtMethodImpl->rgEntries = (bmtMethodImplInfo::Entry *) GetThread()->m_MarshalAlloc.Alloc(
bmtEnumMF->dwNumberMethodImpls * sizeof(bmtMethodImplInfo::Entry));
// 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 < bmtEnumMF->dwNumberMethodImpls; i++)
{
PCCOR_SIGNATURE pSigDecl=NULL,pSigBody = NULL;
ULONG cbSigDecl, cbSigBody;
mdToken tkParent;
mdToken theBody, theDecl;
Substitution theDeclSubst(bmtInternal->pModule, NULL, NULL); // this can get updated later below.
theBody = bmtMetaData->rgMethodImplTokens[i].methodBody;
theDecl = bmtMetaData->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)
{
Module* pModule;
mdToken theNewBody;
hr = FindMethodDeclarationForMethodImpl(theBody,
&theNewBody,
TRUE,
&pModule);
if (FAILED(hr))
{
BuildMethodTableThrowException(hr, IDS_CLASSLOAD_MI_ILLEGAL_BODY, mdMethodDefNil);
}
_ASSERTE(pModule == bmtInternal->pModule);
theBody = theNewBody;
// Make sure to update the stored token with the resolved token.
bmtMetaData->rgMethodImplTokens[i].methodBody = theBody;
}
if (TypeFromToken(theBody) != mdtMethodDef)
{
BuildMethodTableThrowException(BFA_METHODDECL_NOT_A_METHODDEF);
}
CONSISTENCY_CHECK(theBody == bmtMetaData->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
pSigDecl = pMDInternalImport->GetSigOfMethodDef(theDecl,&cbSigDecl);
}
// 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;
szDeclName = pMDInternalImport->GetNameAndSigOfMemberRef(theDecl,&pSigDecl,&cbSigDecl);
// Get parent
hr = pMDInternalImport->GetParentToken(theDecl,&tkParent);
if (FAILED(hr))
BuildMethodTableThrowException(hr, *bmtError);
theDeclSubst = Substitution(tkParent, bmtInternal->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 && cbSigDecl)
{
if((pSigBody = pMDInternalImport->GetSigOfMethodDef(theBody,&cbSigBody)) != NULL && cbSigBody)
{
// Can't use memcmp because there may be two AssemblyRefs
// in this scope, pointing to the same assembly, etc.).
if (!MetaSig::CompareMethodSigs(pSigDecl,
cbSigDecl,
bmtInternal->pModule,
&theDeclSubst,
pSigBody,
cbSigBody,
bmtInternal->pModule,
NULL))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_BODY_DECL_MISMATCH);
}
}
else
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MISSING_SIG_BODY);
}
}
else
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MISSING_SIG_DECL);
}
bmtMetaData->pMethodDeclSubsts[i] = theDeclSubst;
}
}
}
//*******************************************************************************
// Retrieve or add the TokenRange node for a particular token and nodelist.
/*static*/ MethodTableBuilder::bmtTokenRangeNode *MethodTableBuilder::GetTokenRange(mdToken tok, bmtTokenRangeNode **ppHead)
{
CONTRACT (MethodTableBuilder::bmtTokenRangeNode *) {
THROWS;
GC_NOTRIGGER;
INJECT_FAULT(COMPlusThrowOM(););
POSTCONDITION(CheckPointer(RETVAL));
} CONTRACT_END;
BYTE tokrange = ::GetTokenRange(tok);
bmtTokenRangeNode *pWalk = *ppHead;
while (pWalk)
{
if (pWalk->tokenHiByte == tokrange)
{
RETURN pWalk;
}
pWalk = pWalk->pNext;
}
// If we got here, this is the first time we've seen this token range.
bmtTokenRangeNode *pNewNode = (bmtTokenRangeNode*)(GetThread()->m_MarshalAlloc.Alloc(sizeof(bmtTokenRangeNode)));
pNewNode->tokenHiByte = tokrange;
pNewNode->cMethods = 0;
pNewNode->dwCurrentChunk = 0;
pNewNode->dwCurrentIndex = 0;
pNewNode->pNext = *ppHead;
*ppHead = pNewNode;
RETURN pNewNode;
}
//*******************************************************************************
//
// 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(
mdToken pToken, // Token that is being located (MemberRef or MemberDef)
mdToken* pDeclaration, // Method definition for Member
BOOL fSameClass, // Does the declaration need to be in this class
Module** pModule) // Module that the Method Definitions is part of
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
} CONTRACTL_END;
HRESULT hr = S_OK;
IMDInternalImport *pMDInternalImport = bmtInternal->pInternalImport;
// // We are currently assumming that most MethodImpls will be used
// // to define implementation for methods defined on an interface
// // or base type. Therefore, we try to load entry first. If that
// // indicates the member is on our type then we check meta data.
// MethodDesc* pMethod = NULL;
// hr = GetDescFromMemberRef(bmtInternal->pModule,
// pToken,
// GetCl(),
// (void**) (&pMethod),
// bmtError->pThrowable);
// if(FAILED(hr) && !pThrowableAvailable(bmtError->pThrowable)) { // it was us we were find
*pModule = bmtInternal->pModule;
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(pToken) == mdtMemberRef) {
// Get the parent
mdToken typeref = pMDInternalImport->GetParentOfMemberRef(pToken);
GOTPARENT:
// 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 == GetCl());
// This is the real method we are overriding
*pDeclaration = mdtMethodDef;
}
else if (TypeFromToken(typeref) == mdtTypeSpec) {
// Added so that method impls can refer to instantiated interfaces or classes
pSig = pMDInternalImport->GetSigFromToken(typeref, &cSig);
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);
}
goto GOTPARENT;
}
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;
pMDInternalImport->GetNameOfTypeRef(typeref, &pszNameSpace, &pszClassName);
mdToken tkRes = pMDInternalImport->GetResolutionScopeOfTypeRef(typeref);
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(fSameClass && tkDef != GetCl()) {
IfFailRet(COR_E_TYPELOAD);
}
szMember = pMDInternalImport->GetNameAndSigOfMemberRef(pToken,
&pSig,
&cSig);
if(isCallConv(MetaSig::GetCallingConventionInfo(*pModule, pSig),
IMAGE_CEE_CS_CALLCONV_FIELD)) {
return VLDTR_E_MR_BADCALLINGCONV;
}
hr = pMDInternalImport->FindMethodDef(tkDef,
szMember,
pSig,
cSig,
pDeclaration);
IfFailRet(hr);
}
}
else if(TypeFromToken(pToken) == mdtMethodDef) {
mdTypeDef typeDef;
// Verify that we are the parent
hr = pMDInternalImport->GetParentToken(pToken, &typeDef);
IfFailRet(hr);
if(typeDef != GetCl())
{
IfFailRet(COR_E_TYPELOAD);
}
*pDeclaration = pToken;
}
else {
IfFailRet(COR_E_TYPELOAD);
}
return hr;
}
//*******************************************************************************
//
// Used by BuildMethodTable
//
// Enumerate this class's members
//
VOID MethodTableBuilder::EnumerateClassMembers()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
PRECONDITION(CheckPointer(bmtInternal));
PRECONDITION(CheckPointer(bmtEnumMF));
PRECONDITION(CheckPointer(bmtMFDescs));
PRECONDITION(CheckPointer(bmtProp));
PRECONDITION(CheckPointer(bmtMetaData));
PRECONDITION(CheckPointer(bmtVT));
PRECONDITION(CheckPointer(bmtError));
}
CONTRACTL_END;
HRESULT hr = S_OK;
DWORD i;
Thread *pThread = GetThread();
IMDInternalImport *pMDInternalImport = bmtInternal->pInternalImport;
mdToken tok;
DWORD dwMemberAttrs;
BOOL fIsClassEnum = IsEnum();
BOOL fIsClassInterface = IsInterface();
BOOL fIsClassValueType = IsValueClass();
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
bmtMetaData->cMethods = 0;
hr = pMDInternalImport->EnumInit(mdtMethodDef, GetCl(), &(bmtEnumMF->hEnumMethod));
if (FAILED(hr))
{
_ASSERTE(!"Cannot count memberdefs");
if (FAILED(hr))
{
BuildMethodTableThrowException(hr, *bmtError);
}
}
bmtEnumMF->fNeedToCloseEnumMethod = true;
DWORD cbAllocSize = 0;
// Allocate an array to contain the method tokens as well as information about the methods.
bmtMetaData->cMethAndGaps = pMDInternalImport->EnumGetCount(&(bmtEnumMF->hEnumMethod));
if (!ClrSafeInt<DWORD>::multiply(bmtMetaData->cMethAndGaps, sizeof(mdToken), cbAllocSize))
BuildMethodTableThrowException(COR_E_OVERFLOW);
bmtMetaData->pMethods = (mdToken*)pThread->m_MarshalAlloc.Alloc(cbAllocSize);
if (!ClrSafeInt<DWORD>::multiply(bmtMetaData->cMethAndGaps, sizeof(ULONG), cbAllocSize))
BuildMethodTableThrowException(COR_E_OVERFLOW);
bmtMetaData->pMethodRVA = (ULONG*)pThread->m_MarshalAlloc.Alloc(cbAllocSize);
if (!ClrSafeInt<DWORD>::multiply(bmtMetaData->cMethAndGaps, sizeof(DWORD), cbAllocSize))
BuildMethodTableThrowException(COR_E_OVERFLOW);
bmtMetaData->pMethodAttrs = (DWORD*)pThread->m_MarshalAlloc.Alloc(cbAllocSize);
bmtMetaData->pMethodImplFlags = (DWORD*)pThread->m_MarshalAlloc.Alloc(cbAllocSize);
bmtMetaData->pMethodClassifications = (DWORD*)pThread->m_MarshalAlloc.Alloc(cbAllocSize);
if (!ClrSafeInt<DWORD>::multiply(bmtMetaData->cMethAndGaps, sizeof(LPSTR), cbAllocSize))
BuildMethodTableThrowException(COR_E_OVERFLOW);
bmtMetaData->pstrMethodName = (LPCSTR*)pThread->m_MarshalAlloc.Alloc(cbAllocSize);
if (!ClrSafeInt<DWORD>::multiply(bmtMetaData->cMethAndGaps, sizeof(BYTE), cbAllocSize))
BuildMethodTableThrowException(COR_E_OVERFLOW);
bmtMetaData->pMethodImpl = (BYTE*)pThread->m_MarshalAlloc.Alloc(cbAllocSize);
bmtMetaData->pMethodType = (BYTE*)pThread->m_MarshalAlloc.Alloc(cbAllocSize);
enum { SeenCtor = 1, SeenInvoke = 2, SeenBeginInvoke = 4, SeenEndInvoke = 8};
unsigned delegateMethodsSeen = 0;
for (i = 0; i < bmtMetaData->cMethAndGaps; i++)
{
ULONG dwMethodRVA;
DWORD dwImplFlags;
DWORD Classification;
LPSTR strMethodName;
//
// Go to the next method and retrieve its attributes.
//
pMDInternalImport->EnumNext(&(bmtEnumMF->hEnumMethod), &tok);
DWORD rid = RidFromToken(tok);
if ((rid == 0)||(rid > pMDInternalImport->GetCountWithTokenKind(mdtMethodDef)))
{
BuildMethodTableThrowException(BFA_METHOD_TOKEN_OUT_OF_RANGE);
}
dwMemberAttrs = pMDInternalImport->GetMethodDefProps(tok);
if (IsMdRTSpecialName(dwMemberAttrs) || IsMdVirtual(dwMemberAttrs) || IsAnyDelegateClass())
{
strMethodName = (LPSTR)pMDInternalImport->GetNameOfMethodDef(tok);
if(IsStrLongerThan(strMethodName,MAX_CLASS_NAME))
{
BuildMethodTableThrowException(BFA_METHOD_NAME_TOO_LONG);
}
}
else
strMethodName = NULL;
HENUMInternal hEnumTyPars;
hr = pMDInternalImport->EnumInit(mdtGenericParam, tok, &hEnumTyPars);
if (FAILED(hr))
{
BuildMethodTableThrowException(hr, *bmtError);
}
WORD numGenericMethodArgs = (WORD) pMDInternalImport->EnumGetCount(&hEnumTyPars);
// 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)
{
HENUMInternalHolder hEnumGenericPars(pMDInternalImport);
hEnumGenericPars.EnumInit(mdtGenericParam, tok);
for (unsigned methIdx = 0; methIdx < numGenericMethodArgs; methIdx++)
{
mdGenericParam tkTyPar;
pMDInternalImport->EnumNext(&hEnumGenericPars, &tkTyPar);
DWORD flags;
pMDInternalImport->GetGenericParamProps(tkTyPar, NULL, &flags, NULL, NULL, NULL);
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);
}
}
pMDInternalImport->EnumClose(&hEnumGenericPars);
}
//
// 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))
{
// 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)
{
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,
mdMethodDefNil);
}
// Skip '_'.
pos++;
// Read count.
while (IS_DIGIT(*pos))
{
_ASSERTE(n < 6552);
n *= 10;
n += DIGIT_TO_INT(*pos);
pos++;
}
// Check for end of name.
if (*pos != '\0')
{
BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT,
BFA_METHOD_NAME_NOT_TERMINATED,
mdMethodDefNil);
}
}
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.
//
pMDInternalImport->GetMethodImplProps(tok, &dwMethodRVA, &dwImplFlags);
//
// But first - minimal flags validity checks
//
// No methods in Enums!
if(fIsClassEnum)
{
BuildMethodTableThrowException(BFA_METHOD_IN_A_ENUM);
}
// RVA : 0
if(dwMethodRVA != 0)
{
if(IsMdAbstract(dwMemberAttrs))
{
BuildMethodTableThrowException(BFA_ABSTRACT_METHOD_WITH_RVA);
}
if(IsMiRuntime(dwImplFlags))
{
BuildMethodTableThrowException(BFA_RUNTIME_METHOD_WITH_RVA);
}
if(IsMiInternalCall(dwImplFlags))
{
BuildMethodTableThrowException(BFA_INTERNAL_METHOD_WITH_RVA);
}
}
// Abstract / not abstract
if(IsMdAbstract(dwMemberAttrs))
{
if(fIsClassNotAbstract)
{
BuildMethodTableThrowException(BFA_AB_METHOD_IN_AB_CLASS);
}
if(!IsMdVirtual(dwMemberAttrs))
{
BuildMethodTableThrowException(BFA_NONVIRT_AB_METHOD);
}
}
else if(fIsClassInterface && strMethodName &&
(strcmp(strMethodName, COR_CCTOR_METHOD_NAME)))
{
BuildMethodTableThrowException(BFA_NONAB_NONCCTOR_METHOD_ON_INT);
}
// Virtual / not virtual
if(IsMdVirtual(dwMemberAttrs))
{
if(IsMdPinvokeImpl(dwMemberAttrs))
{
BuildMethodTableThrowException(BFA_VIRTUAL_PINVOKE_METHOD);
}
if(IsMdStatic(dwMemberAttrs))
{
BuildMethodTableThrowException(BFA_VIRTUAL_STATIC_METHOD);
}
if(strMethodName && (0==strcmp(strMethodName, COR_CTOR_METHOD_NAME)))
{
BuildMethodTableThrowException(BFA_VIRTUAL_INSTANCE_CTOR);
}
}
// Some interface checks.
if (IsInterface())
{
if (IsMdVirtual(dwMemberAttrs))
{
if (!IsMdAbstract(dwMemberAttrs))
{
BuildMethodTableThrowException(BFA_VIRTUAL_NONAB_INT_METHOD);
}
}
else
{
// Instance field/method
if (!IsMdStatic(dwMemberAttrs))
{
BuildMethodTableThrowException(BFA_NONVIRT_INST_INT_METHOD);
}
}
}
// No synchronized methods in ValueTypes
if(fIsClassValueType && IsMiSynchronized(dwImplFlags))
{
BuildMethodTableThrowException(BFA_SYNC_METHOD_IN_VT);
}
// Global methods:
if(IsGlobalClass())
{
if(!IsMdStatic(dwMemberAttrs))
{
BuildMethodTableThrowException(BFA_NONSTATIC_GLOBAL_METHOD);
}
if (strMethodName)
{
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 ((bmtGenerics->GetNumGenericArgs() != 0 || numGenericMethodArgs != 0) &&
(
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
pMemberSignature = pMDInternalImport->GetSigOfMethodDef(tok,&cMemberSignature);
hr = validateTokenSig(tok,pMemberSignature,cMemberSignature,dwMemberAttrs,pMDInternalImport);
if (FAILED(hr))
{
BuildMethodTableThrowException(hr, BFA_BAD_SIGNATURE, mdMethodDefNil);
}
// Check the appearance of covariant and contravariant in the method signature
// Note that variance is only supported for interfaces
if (bmtGenerics->pVarianceInfo != NULL)
{
SigPointer sp(pMemberSignature);
ULONG callConv;
IfFailThrow(sp.GetCallingConvInfo(&callConv));
if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
IfFailThrow(sp.GetData(NULL));
DWORD numArgs;
IfFailThrow(sp.GetData(&numArgs));
// Return type behaves covariantly
if (!GetHalfBakedClass()->CheckVarianceInSig(bmtGenerics->GetNumGenericArgs(), bmtGenerics->pVarianceInfo, sp, gpCovariant))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_VARIANCE_IN_METHOD_RESULT, tok);
}
sp.SkipExactlyOne();
for (DWORD j = 0; j < numArgs; j++)
{
// Argument types behave contravariantly
if (!GetHalfBakedClass()->CheckVarianceInSig(bmtGenerics->GetNumGenericArgs(), bmtGenerics->pVarianceInfo,
sp, gpContravariant))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_VARIANCE_IN_METHOD_ARG, tok);
}
sp.SkipExactlyOne();
}
}
//
// Determine the method's classification.
//
if (IsReallyMdPinvokeImpl(dwMemberAttrs) || IsMiInternalCall(dwImplFlags))
{
hr = NDirect::HasNAT_LAttribute(pMDInternalImport, tok);
if (FAILED(hr))
{
BuildMethodTableThrowException(hr, IDS_CLASSLOAD_BADPINVOKE, tok);
}
if (hr == S_FALSE)
{
if (dwMethodRVA == 0)
Classification = mcFCall;
else
Classification = mcNDirect;
}
else
Classification = mcNDirect;
}
else if (IsMiRuntime(dwImplFlags))
{
// currently the only runtime implemented functions are delegate instance methods
if (!IsAnyDelegateClass() || 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)
{
// This codepath is used by remoting
Classification = mcIL;
}
else
{
Classification = mcIL;
}
#ifdef _DEBUG
// We don't allow stack based declarative security on ecalls, fcalls and
// other special purpose methods implemented by the EE (the interceptor
// we use doesn't play well with non-jitted stubs).
if ((Classification == mcFCall || Classification == mcEEImpl) &&
(IsMdHasSecurity(dwMemberAttrs) || IsTdHasSecurity(GetAttrClass())))
{
DWORD dwSecFlags;
DWORD dwNullDeclFlags;
if (IsTdHasSecurity(GetAttrClass()) &&
SUCCEEDED(Security::GetDeclarationFlags(pMDInternalImport, GetCl(), &dwSecFlags, &dwNullDeclFlags)))
{
CONSISTENCY_CHECK_MSG(!(dwSecFlags & ~dwNullDeclFlags & DECLSEC_RUNTIME_ACTIONS),
"Cannot add stack based declarative security to a class containing an ecall/fcall/special method.");
}
if (IsMdHasSecurity(dwMemberAttrs) &&
SUCCEEDED(Security::GetDeclarationFlags(pMDInternalImport, tok, &dwSecFlags, &dwNullDeclFlags)))
{
CONSISTENCY_CHECK_MSG(!(dwSecFlags & ~dwNullDeclFlags & DECLSEC_RUNTIME_ACTIONS),
"Cannot add stack based declarative security to an ecall/fcall/special method.");
}
}
#endif // _DEBUG
// Generic methods should always be mcInstantiated
if (!((numGenericMethodArgs == 0) || ((Classification & mdcClassification) == mcInstantiated)))
{
BuildMethodTableThrowException(BFA_GENERIC_METHODS_INST);
}
// count how many overrides this method does All methods bodies are defined
// on this type so we can just compare the tok with the body token found
// from the overrides.
for(DWORD impls = 0; impls < bmtEnumMF->dwNumberMethodImpls; impls++) {
if(bmtMetaData->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 (IsAnyDelegateClass() && !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;
}
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.
//
bmtMetaData->SetMethodData(NumDeclaredMethods(),
tok,
dwMemberAttrs,
dwMethodRVA,
dwImplFlags,
Classification,
strMethodName,
impl,
type);
IncNumDeclaredMethods();
//
// Update the count of the various types of methods.
//
bmtVT->dwMaxVtableSize++;
// Increment the number of non-abstract declared methods
if (!IsMdAbstract(dwMemberAttrs))
{
bmtEnumMF->dwNumDeclaredNonAbstractMethods++;
}
// Increment the number of IL instance methods
if (type == METHOD_TYPE_NORMAL && !IsMdStatic(dwMemberAttrs))
{
bmtEnumMF->dwNumILInstanceMethods++;
}
// Increment the number of MethodDescs for this type of method
bmtMFDescs->sets[type][impl].dwNumMethodDescs++;
// If the method requires an unboxing method, record this fact
BOOL hasUnboxing = (IsValueClass()
&& !IsMdStatic(dwMemberAttrs)
&& type != METHOD_TYPE_INSTANTIATED
&& IsMdVirtual(dwMemberAttrs)
&& !IsMdRTSpecialName(dwMemberAttrs));
if (hasUnboxing)
{
bmtEnumMF->dwNumUnboxingMethods++;
bmtMFDescs->sets[type][impl].dwNumBoxedEntryPointMDs++;
}
// Update the token ranges for this type of method to be used for MethodDescChunk allocation
GetTokenRange(tok, &(bmtMetaData->ranges[type][impl]))->cMethods += (hasUnboxing ? 2 : 1);
}
// Check to see that we have all of the required delegate methods (ECMA 13.6 Delegates)
if (IsAnyDelegateClass())
{
// 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 != bmtMetaData->cMethAndGaps)
{
BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_METHOD_COUNT, mdTokenNil);
}
pMDInternalImport->EnumReset(&(bmtEnumMF->hEnumMethod));
//
// Run through the field list and calculate the following:
// # static fields
// # static fields that contain object refs.
// # instance fields
//
bmtEnumMF->dwNumStaticFields = 0;
bmtEnumMF->dwNumStaticObjRefFields = 0;
bmtEnumMF->dwNumStaticBoxedFields = 0;
bmtEnumMF->dwNumInstanceFields = 0;
hr = pMDInternalImport->EnumInit(mdtFieldDef, GetCl(), &(bmtEnumMF->hEnumField));
if (FAILED(hr))
{
BuildMethodTableThrowException(hr, *bmtError);
}
bmtMetaData->cFields = pMDInternalImport->EnumGetCount(&(bmtEnumMF->hEnumField));
bmtEnumMF->fNeedToCloseEnumField = true;
// Retrieve the fields and store them in a temp array.
bmtMetaData->pFields = (mdToken*)pThread->m_MarshalAlloc.Alloc(bmtMetaData->cFields * sizeof(mdToken));
bmtMetaData->pFieldAttrs = (DWORD*)pThread->m_MarshalAlloc.Alloc(bmtMetaData->cFields * sizeof(DWORD));
DWORD dwFieldLiteralInitOnly = fdLiteral | fdInitOnly;
for (i = 0; pMDInternalImport->EnumNext(&(bmtEnumMF->hEnumField), &tok); i++)
{
//
// Retrieve the attributes of the field.
//
DWORD rid = tok & 0x00FFFFFF;
if ((rid == 0)||(rid > pMDInternalImport->GetCountWithTokenKind(mdtFieldDef)))
{
BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, BFA_BAD_FIELD_TOKEN, mdTokenNil);
}
dwMemberAttrs = pMDInternalImport->GetFieldDefProps(tok);
//
// Store the field and its attributes in the bmtMetaData structure for later use.
//
bmtMetaData->pFields[i] = tok;
bmtMetaData->pFieldAttrs[i] = dwMemberAttrs;
if((dwMemberAttrs & fdFieldAccessMask)==fdFieldAccessMask)
{
BuildMethodTableThrowException(BFA_INVALID_FIELD_ACC_FLAGS);
}
if((dwMemberAttrs & dwFieldLiteralInitOnly)==dwFieldLiteralInitOnly)
{
BuildMethodTableThrowException(BFA_FIELD_LITERAL_AND_INIT);
}
// can only have static global fields
if(IsGlobalClass())
{
if(!IsMdStatic(dwMemberAttrs))
{
BuildMethodTableThrowException(BFA_NONSTATIC_GLOBAL_FIELD);
}
}
//
// Update the count of the various types of fields.
//
if (IsFdStatic(dwMemberAttrs))
{
if (!IsFdLiteral(dwMemberAttrs))
{
bmtEnumMF->dwNumStaticFields++;
}
}
else
{
bmtEnumMF->dwNumInstanceFields++;
if(fIsClassInterface)
{
BuildMethodTableThrowException(BFA_INSTANCE_FIELD_IN_INT);
}
}
}
if (i != bmtMetaData->cFields)
{
BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_FIELD_COUNT, mdTokenNil);
}
if(fIsClassEnum && (bmtEnumMF->dwNumInstanceFields==0))
{
BuildMethodTableThrowException(BFA_INSTANCE_FIELD_IN_ENUM);
}
bmtEnumMF->dwNumDeclaredFields = bmtEnumMF->dwNumStaticFields + bmtEnumMF->dwNumInstanceFields;
}
//*******************************************************************************
//
// Used by AllocateMethodFieldDescs
// Thus used by BuildMethodTable
//
// Allocates the chunks used to contain the method descs.
//
MethodDescChunk ** MethodTableBuilder::AllocateMDChunks(
bmtTokenRangeNode *pTokenRanges, DWORD type, DWORD impl, DWORD *pNumChunks, AllocMemTracker *pamTracker)
{
CONTRACTL
{
THROWS;
GC_NOTRIGGER;
INJECT_FAULT(COMPlusThrowOM(););
PRECONDITION(CheckPointer(this));
PRECONDITION(CheckPointer(pTokenRanges));
}
CONTRACTL_END;
static const DWORD classifications[METHOD_TYPE_COUNT][METHOD_IMPL_COUNT] =
{
{ mcIL, mcIL | mdcMethodImpl },
{ mcFCall, mcFCall | mdcMethodImpl },
{ mcEEImpl, mcEEImpl | mdcMethodImpl },
{ mcNDirect, mcNDirect | mdcMethodImpl }
, { mcInstantiated, mcInstantiated | mdcMethodImpl }
};
DWORD Classification = classifications[type][impl];
bmtTokenRangeNode *pTR = pTokenRanges;
*pNumChunks = 0;
while (pTR)
{
// Note: Since dwCurrentChunk isn't being used at this stage, we'll steal it to store
// away the chunk count.
// After this function, we'll set it to its intended value.
pTR->dwCurrentChunk = MethodDescChunk::GetChunkCount(pTR->cMethods, Classification);
(*pNumChunks) += pTR->dwCurrentChunk;
pTR = pTR->pNext;
}
MethodDescChunk **pItfMDChunkList = (MethodDescChunk**)GetThread()->m_MarshalAlloc.Alloc((*pNumChunks) * sizeof(MethodDescChunk*));
// Allocate the chunks for the method descs.
pTR = pTokenRanges;
DWORD chunkIdx = 0;
while (pTR)
{
DWORD NumChunks = pTR->dwCurrentChunk;
DWORD dwMDAllocs = pTR->cMethods;
pTR->dwCurrentChunk = chunkIdx;
for (DWORD i = 0; i < NumChunks; i++)
{
DWORD dwElems = min(dwMDAllocs, MethodDescChunk::GetMaxMethodDescs(Classification));
MethodDescChunk *pChunk = MethodDescChunk::CreateChunk(bmtDomain->GetHighFrequencyHeap(),
dwElems,
Classification,
pTR->tokenHiByte,
NULL,
pamTracker);
pItfMDChunkList[chunkIdx++] = pChunk;
dwMDAllocs -= dwElems;
}
pTR = pTR->pNext;
}
return pItfMDChunkList;
}
//*******************************************************************************
//
// 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::AllocateWorkingSlotTables()
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
PRECONDITION(CheckPointer(this));
PRECONDITION(CheckPointer(bmtDomain));
PRECONDITION(CheckPointer(bmtMFDescs));
PRECONDITION(CheckPointer(bmtMetaData));
PRECONDITION(CheckPointer(bmtVT));
PRECONDITION(CheckPointer(bmtEnumMF));
PRECONDITION(CheckPointer(bmtInterface));
PRECONDITION(CheckPointer(bmtFP));
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
bmtMFDescs->ppMethodDescList = (MethodDesc **) pThread->m_MarshalAlloc.Alloc(NumDeclaredMethods() * sizeof(MethodDesc *));
ZeroMemory(bmtMFDescs->ppMethodDescList, NumDeclaredMethods() * sizeof(MethodDesc *));
bmtMFDescs->ppFieldDescList = (FieldDesc **) pThread->m_MarshalAlloc.Alloc(bmtMetaData->cFields * sizeof(FieldDesc *));
ZeroMemory(bmtMFDescs->ppFieldDescList, bmtMetaData->cFields * sizeof(FieldDesc *));
// 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();
bmtMFDescs->ppUnboxMethodDescList =
(MethodDesc **) pThread->m_MarshalAlloc.Alloc(NumDeclaredMethods() * sizeof(MethodDesc*));
ZeroMemory(bmtMFDescs->ppUnboxMethodDescList, NumDeclaredMethods() * sizeof(MethodDesc*));
}
// sanity check
_ASSERTE(bmtParent->pParentMethodTable == NULL || (bmtInterface->dwInterfaceMapSize - bmtParent->pParentMethodTable->GetNumInterfaces()) >= 0);
// add parent vtable size
bmtVT->dwMaxVtableSize += bmtVT->dwCurrentVtableSlot;
for (i = 0; i < bmtInterface->dwInterfaceMapSize; 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 = (SLOT *) pThread->m_MarshalAlloc.Alloc(bmtVT->dwMaxVtableSize * sizeof(SLOT));
ZeroMemory(bmtVT->pVtable, bmtVT->dwMaxVtableSize * sizeof(SLOT));
bmtVT->pVtableMD = (MethodDesc**) pThread->m_MarshalAlloc.Alloc(bmtVT->dwMaxVtableSize * sizeof(SLOT));
ZeroMemory(bmtVT->pVtableMD, bmtVT->dwMaxVtableSize * sizeof(MethodDesc*));
// Allocate the temporary non-vtable
bmtVT->pNonVtableMD = (MethodDesc**) pThread->m_MarshalAlloc.Alloc(sizeof(MethodDesc*) * NumDeclaredMethods());
ZeroMemory(bmtVT->pNonVtableMD, sizeof(MethodDesc*) * NumDeclaredMethods());
if (bmtParent->pParentMethodTable != NULL)
{
if (bmtParent->pParentMethodTable->IsZapped())
{
for (unsigned j=0; j<bmtParent->pParentMethodTable->GetNumVirtuals(); j++)
{
bmtParent->pParentMethodTable->GetRestoredSlot(j);
}
}
// 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] = (SLOT) it.GetTarget().GetTarget();
bmtVT->pVtableMD[slot] = NULL; // MethodDescs are resolved lazily
}
bmtVT->pParentMethodTable = bmtParent->pParentMethodTable;
}
}
if (NumDeclaredMethods() > 0)
{
bmtParent->ppParentMethodDescBuf = (MethodDesc **)
pThread->m_MarshalAlloc.Alloc(2 * NumDeclaredMethods() *
sizeof(MethodDesc*));
bmtParent->ppParentMethodDescBufPtr = bmtParent->ppParentMethodDescBuf;
}
}
//*******************************************************************************
//
// Used by BuildMethodTable
//
// Allocate a MethodDesc* for each method (needed later when doing interfaces), and a FieldDesc* for each field
//
VOID MethodTableBuilder::AllocateMethodFieldDescs(AllocMemTracker *pamTracker)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
PRECONDITION(CheckPointer(this));
PRECONDITION(CheckPointer(bmtDomain));
PRECONDITION(CheckPointer(bmtMFDescs));
PRECONDITION(CheckPointer(bmtMetaData));
PRECONDITION(CheckPointer(bmtVT));
PRECONDITION(CheckPointer(bmtEnumMF));
PRECONDITION(CheckPointer(bmtFP));
PRECONDITION(CheckPointer(bmtParent));
}
CONTRACTL_END;
DWORD i;
// We'll be counting the # fields of each size as we go along
for (i = 0; i <= MAX_LOG2_PRIMITIVE_FIELD_SIZE; i++)
{
bmtFP->NumStaticFieldsOfSize[i] = 0;
bmtFP->NumInstanceFieldsOfSize[i] = 0;
}
//
// Allocate blocks of MethodDescs and FieldDescs for all declared methods and fields
//
// In order to avoid allocating a field pointing back to the method
// table in every single method desc, we allocate memory in the
// following manner:
// o Field descs get a single contiguous block.
// o Method descs of different sizes (normal vs NDirect) are
// allocated in different MethodDescChunks.
// o Each method desc chunk starts with a header, and has
// at most MAX_ method descs (if there are more
// method descs of a given size, multiple chunks are allocated).
// This way method descs can use an 8-bit offset field to locate the
// pointer to their method table.
//
// Allocate fields first.
if (NumDeclaredFields() > 0)
{
GetHalfBakedClass()->m_pFieldDescList = (FieldDesc *)pamTracker->Track(
bmtDomain->GetHighFrequencyHeap()->AllocMem(NumDeclaredFields() *
sizeof(FieldDesc)));
INDEBUG(GetClassLoader()->m_dwDebugFieldDescs += NumDeclaredFields();)
INDEBUG(GetClassLoader()->m_dwFieldDescData += (NumDeclaredFields() * sizeof(FieldDesc));)
}
else
{
// No fields or methods
GetHalfBakedClass()->m_pFieldDescList = NULL;
}
if (NumDeclaredMethods() > 0)
{
for (DWORD impl=0; impl<METHOD_IMPL_COUNT; impl++)
{
for (DWORD type=0; type<METHOD_TYPE_COUNT; type++)
{
bmtMethodDescSet *set = &bmtMFDescs->sets[type][impl];
DWORD dwAllocs = set->dwNumMethodDescs + set->dwNumBoxedEntryPointMDs;
if (dwAllocs > 0)
{
set->pChunkList = AllocateMDChunks(
bmtMetaData->ranges[type][impl],
type, impl,
&set->dwChunks,
pamTracker);
}
#ifdef _DEBUG
GetClassLoader()->m_dwDebugMethods += dwAllocs;
for (UINT j=0; j<set->dwChunks; j++)
GetClassLoader()->m_dwMethodDescData += set->pChunkList[j]->Sizeof();
#endif // _DEBUG
}
}
}
}
//*******************************************************************************
//
// Heuristic to determine if we should have instances of this class 8 byte aligned
//
BOOL MethodTableBuilder::ShouldAlign8(DWORD dwR8Fields, DWORD dwTotalFields)
{
LEAF_CONTRACT;
return dwR8Fields*2>dwTotalFields && dwR8Fields>=2;
}
//*******************************************************************************
BOOL MethodTableBuilder::IsSelfReferencingStaticValueTypeField(mdToken dwByValueClassToken,
bmtInternalInfo* bmtInternal,
const bmtGenericsInfo *bmtGenerics,
PCCOR_SIGNATURE pMemberSignature,
DWORD cMemberSignature)
{
CONTRACTL
{
THROWS; // see note below as to why this is NOTHROW
WRAPPER(GC_TRIGGERS);
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END;
if (dwByValueClassToken != this->GetCl())
{
return FALSE;
}
if (!bmtGenerics->HasInstantiation())
{
return TRUE;
}
// <NOTE>See notes in InitializeFieldDescs as to why we bail out here </NOTE>
if (bmtInternal->pModule->IsEditAndContinueEnabled() && GetThread() == NULL)
{
return FALSE;
}
// The value class is generic. Check that the signature of the field
// is _exactly_ equivalent to VC<!0, !1, !2, ...>. Do this by consing up a fake
// signature.
DWORD nGenericArgs = bmtGenerics->GetNumGenericArgs();
CONSISTENCY_CHECK(nGenericArgs != 0);
unsigned int nFakeSig = 1 + 1 + 4 + 4 + (1+4) * nGenericArgs;
BYTE *pFakeSigMem = (BYTE *) _alloca(nFakeSig);
BYTE *pFakeSigMemMax = pFakeSigMem + nFakeSig;
PCCOR_SIGNATURE pFieldSig = pMemberSignature + 1; // skip the CALLCONV_FIELD
PCCOR_SIGNATURE pFakeSig = (PCCOR_SIGNATURE) pFakeSigMem;
PCCOR_SIGNATURE pFakeSigMax = (PCCOR_SIGNATURE) pFakeSigMemMax;
/* 1 */ pFakeSigMem += CorSigCompressElementTypeSafe(ELEMENT_TYPE_GENERICINST,pFakeSigMem, pFakeSigMemMax);
/* 1 */ pFakeSigMem += CorSigCompressElementTypeSafe(ELEMENT_TYPE_VALUETYPE,pFakeSigMem, pFakeSigMemMax);
/* 4 */ pFakeSigMem += CorSigCompressTokenSafe(dwByValueClassToken,pFakeSigMem, pFakeSigMemMax);
/* max 4 */ pFakeSigMem += CorSigCompressDataSafe(nGenericArgs, pFakeSigMem, pFakeSigMemMax);
for (unsigned int typearg = 0; typearg < nGenericArgs; typearg++)
{
/* 1 */ pFakeSigMem += CorSigCompressElementTypeSafe(ELEMENT_TYPE_VAR,pFakeSigMem, pFakeSigMemMax);
/* max 4 */ pFakeSigMem += CorSigCompressDataSafe(typearg, pFakeSigMem, pFakeSigMemMax);
}
return MetaSig::CompareElementType(pFakeSig, pFieldSig,
pFakeSigMax, pMemberSignature + cMemberSignature,
bmtInternal->pModule, bmtInternal->pModule,
NULL, NULL,
NULL, NULL);
}
//*******************************************************************************
//
// Used by BuildMethodTable
//
// Go thru all fields and initialize their FieldDescs.
//
VOID MethodTableBuilder::InitializeFieldDescs(BaseDomain *bmtDomain,
FieldDesc *pFieldDescList,
const LayoutRawFieldInfo* pLayoutRawFieldInfos,
bmtInternalInfo* bmtInternal,
const bmtGenericsInfo* bmtGenerics,
bmtMetaDataInfo* bmtMetaData,
bmtEnumMethAndFields* bmtEnumMF,
bmtErrorInfo* bmtError,
EEClass*** pByValueClassCache,
bmtMethAndFieldDescs* bmtMFDescs,
bmtFieldPlacement* bmtFP,
bmtThreadContextStaticInfo* pbmtTCSInfo,
unsigned* totalDeclaredSize,
bmtParentInfo* bmtParent)
{
CONTRACTL
{
// InitializeFieldDescs/IsSelfReferencingStaticValueTypeField
// should by rights be THROWS because
// (a) they call the loader and
// (b) IsSelfReferencingStaticValueTypeField calls signature comparison functions which in
// turn call the loader.
// We could simply propagate the exceptions
// if it weren't for the fact that InitializeFieldDescs is used by EnC to initialize
// freshly added field descriptors, and thus it is called from the debugger thread
// which cannot handle these exceptions.
//
// So further below we explicitly catch exceptions at the calls to the loader
// functions (actually we call the non-throwing entry points to the loader),
// and also avoid calling the loader at all when executing the EnC paths.
THROWS;
WRAPPER(GC_TRIGGERS);
INJECT_FAULT(COMPlusThrowOM(););
PRECONDITION(CheckPointer(this));
PRECONDITION(CheckPointer(bmtInternal));
PRECONDITION(CheckPointer(bmtGenerics));
PRECONDITION(CheckPointer(bmtMetaData));
PRECONDITION(CheckPointer(bmtEnumMF));
PRECONDITION(CheckPointer(bmtError));
PRECONDITION(CheckPointer(pByValueClassCache));
PRECONDITION(CheckPointer(bmtMFDescs));
PRECONDITION(CheckPointer(bmtFP));
PRECONDITION(CheckPointer(totalDeclaredSize));
PRECONDITION(CheckPointer(bmtParent, NULL_OK));
}
CONTRACTL_END;
DWORD i;
IMDInternalImport *pInternalImport = bmtInternal->pInternalImport; // to avoid multiple dereferencings
FieldMarshaler *pNextFieldMarshaler = NULL;
if (HasLayout())
{
pNextFieldMarshaler = (FieldMarshaler*)(GetLayoutInfo()->GetFieldMarshalers());
}
//========================================================================
// BEGIN:
// Go thru all fields and initialize their FieldDescs.
//========================================================================
DWORD dwCurrentDeclaredField = 0;
DWORD dwCurrentStaticField = 0;
DWORD dwThreadStaticsOffset = 0;
DWORD dwContextStaticsOffset = 0;
DWORD dwR8Fields = 0; // Number of R8's the class has
#ifdef RVA_FIELD_VALIDATION_ENABLED
Module* pMod = bmtInternal->pModule;
#endif
for (i = 0; i < bmtMetaData->cFields; i++)
{
PCCOR_SIGNATURE pMemberSignature;
DWORD cMemberSignature;
DWORD dwMemberAttrs;
dwMemberAttrs = bmtMetaData->pFieldAttrs[i];
// We don't store static final primitive fields in the class layout
if (IsFdLiteral(dwMemberAttrs))
continue;
if(!IsFdPublic(dwMemberAttrs))
SetHasNonPublicFields();
if (IsFdNotSerialized(dwMemberAttrs))
SetCannotBeBlittedByObjectCloner();
pMemberSignature = pInternalImport->GetSigOfFieldDef(bmtMetaData->pFields[i], &cMemberSignature);
// Signature validation
IfFailThrow(validateTokenSig(bmtMetaData->pFields[i],pMemberSignature,cMemberSignature,dwMemberAttrs,pInternalImport));
FieldDesc * pFD;
DWORD dwLog2FieldSize = 0;
BOOL bCurrentFieldIsGCPointer = FALSE;
mdToken dwByValueClassToken = 0;
EEClass * pByValueClass = NULL;
BOOL fIsByValue = FALSE;
BOOL fIsThreadStatic = FALSE;
BOOL fIsContextStatic = FALSE;
BOOL fHasRVA = FALSE;
MetaSig fsig(pMemberSignature,
cMemberSignature,
bmtInternal->pModule,
&bmtGenerics->typeContext,
FALSE,
MetaSig::sigField);
CorElementType ElementType = fsig.NextArg();
// Get type
if (!isCallConv(fsig.GetCallingConvention(), IMAGE_CEE_CS_CALLCONV_FIELD))
{
IfFailThrow(COR_E_TYPELOAD);
}
// Determine if a static field is special i.e. RVA based, local to
// a thread or a context
if(IsFdStatic(dwMemberAttrs))
{
if(IsFdHasFieldRVA(dwMemberAttrs))
{
fHasRVA = TRUE;
}
HRESULT hr;
hr = pInternalImport->GetCustomAttributeByName(bmtMetaData->pFields[i],
g_ThreadStaticAttributeClassName,
NULL, NULL);
IfFailThrow(hr);
if (hr == S_OK)
{
fIsThreadStatic = TRUE;
}
hr = pInternalImport->GetCustomAttributeByName(bmtMetaData->pFields[i],
g_ContextStaticAttributeClassName,
NULL, NULL);
IfFailThrow(hr);
if (hr == S_OK)
{
fIsContextStatic = TRUE;
}
if(ElementType == ELEMENT_TYPE_VALUETYPE)
{
hr = pInternalImport->GetCustomAttributeByName(bmtMetaData->pFields[i],
g_CompilerServicesFixedAddressValueTypeAttribute,
NULL, NULL);
IfFailThrow(hr);
if (hr == S_OK)
{
bmtFP->fHasFixedAddressValueTypes = true;
}
}
// Do some sanity checks that we are not mixing context and thread
// relative statics.
if (fIsThreadStatic && fIsContextStatic)
{
IfFailThrow(COR_E_TYPELOAD);
}
if (fHasRVA && (fIsThreadStatic || fIsContextStatic))
{
IfFailThrow(COR_E_TYPELOAD);
}
}
GOT_ELEMENT_TYPE:
// Type to store in FieldDesc - we don't want to have extra case statements for
// ELEMENT_TYPE_STRING, SDARRAY etc., so we convert all object types to CLASS.
// Also, BOOLEAN, CHAR are converted to U1, I2.
CorElementType FieldDescElementType = ElementType;
switch (ElementType)
{
case ELEMENT_TYPE_I1:
case ELEMENT_TYPE_U1:
{
dwLog2FieldSize = 0;
break;
}
case ELEMENT_TYPE_I2:
case ELEMENT_TYPE_U2:
{
dwLog2FieldSize = 1;
break;
}
case ELEMENT_TYPE_I4:
case ELEMENT_TYPE_U4:
IN_WIN32(case ELEMENT_TYPE_I:)
IN_WIN32(case ELEMENT_TYPE_U:)
case ELEMENT_TYPE_R4:
{
dwLog2FieldSize = 2;
break;
}
case ELEMENT_TYPE_BOOLEAN:
{
// FieldDescElementType = ELEMENT_TYPE_U1;
dwLog2FieldSize = 0;
break;
}
case ELEMENT_TYPE_CHAR:
{
// FieldDescElementType = ELEMENT_TYPE_U2;
dwLog2FieldSize = 1;
break;
}
case ELEMENT_TYPE_R8:
dwR8Fields++;
// Fall through
case ELEMENT_TYPE_I8:
case ELEMENT_TYPE_U8:
IN_WIN64(case ELEMENT_TYPE_I:)
IN_WIN64(case ELEMENT_TYPE_U:)
{
dwLog2FieldSize = 3;
break;
}
case ELEMENT_TYPE_FNPTR:
case ELEMENT_TYPE_PTR: // ptrs are unmanaged scalars, for layout
{
dwLog2FieldSize = LOG2_PTRSIZE;
break;
}
// Class type variable (method type variables aren't allowed in fields)
// These only occur in open types used for verification/reflection.
case ELEMENT_TYPE_VAR:
case ELEMENT_TYPE_MVAR:
// deliberate drop through - do fake field layout
case ELEMENT_TYPE_STRING:
case ELEMENT_TYPE_SZARRAY: // single dim, zero
case ELEMENT_TYPE_ARRAY: // all other arrays
case ELEMENT_TYPE_CLASS: // objectrefs
case ELEMENT_TYPE_OBJECT:
{
dwLog2FieldSize = LOG2_PTRSIZE;
bCurrentFieldIsGCPointer = TRUE;
FieldDescElementType = ELEMENT_TYPE_CLASS;
if (IsFdStatic(dwMemberAttrs) == 0)
{
SetHasFieldsWhichMustBeInited();
if (ElementType != ELEMENT_TYPE_STRING)
SetCannotBeBlittedByObjectCloner();
}
else
{
// Increment the number of static fields that contain object references.
bmtEnumMF->dwNumStaticObjRefFields++;
}
break;
}
case ELEMENT_TYPE_VALUETYPE: // a byvalue class field
{
dwByValueClassToken = fsig.GetArgProps().PeekValueTypeTokenClosed(&bmtGenerics->typeContext);
fIsByValue = TRUE;
// By-value class
BAD_FORMAT_NOTHROW_ASSERT(dwByValueClassToken != 0);
#ifndef RVA_FIELD_VALIDATION_ENABLED
if (fHasRVA)
break;
#endif // !RVA_FIELD_VALIDATION_ENABLED
if (this->IsValueClass())
{
BOOL selfref = IsSelfReferencingStaticValueTypeField(dwByValueClassToken,
bmtInternal,
bmtGenerics,
pMemberSignature,
cMemberSignature);
if (selfref)
{
// immediately self-referential machines must be static.
if (!IsFdStatic(dwMemberAttrs))
{
bmtError->resIDWhy = IDS_CLASSLOAD_VALUEINSTANCEFIELD;
COMPlusThrowHR(COR_E_TYPELOAD);
}
pByValueClass = GetHalfBakedClass();
}
else
{
// We also check the TypeRef case, though this is in theory invalid IL (using a typeRef
// to refer to something defined in the same module) and no compilers should be producing it.
// <NICE> Get rid of this </NICE>
if (IsFdStatic(dwMemberAttrs) && (TypeFromToken(dwByValueClassToken) == mdtTypeRef))
{
// It's a typeref - check if it's a class that has a static field of itself
mdTypeDef ValueCL;
LPCUTF8 pszNameSpace;
LPCUTF8 pszClassName;
pInternalImport->GetNameOfTypeRef(dwByValueClassToken, &pszNameSpace, &pszClassName);
if(IsStrLongerThan((char*)pszClassName,MAX_CLASS_NAME)
|| IsStrLongerThan((char*)pszNameSpace,MAX_CLASS_NAME)
|| (strlen(pszClassName)+strlen(pszNameSpace)+1 >= MAX_CLASS_NAME))
{
COMPlusThrowHR(COR_E_TYPELOAD, BFA_TYPEREG_NAME_TOO_LONG);
}
mdToken tkRes = pInternalImport->GetResolutionScopeOfTypeRef(dwByValueClassToken);
if(TypeFromToken(tkRes) == mdtTypeRef)
{
DWORD rid = RidFromToken(tkRes);
if((rid==0)||(rid > pInternalImport->GetCountWithTokenKind(mdtTypeRef)))
{
COMPlusThrowHR(COR_E_TYPELOAD, BFA_BAD_TYPEREF_TOKEN);
}
}
else tkRes = mdTokenNil;
if (SUCCEEDED(pInternalImport->FindTypeDef(pszNameSpace,
pszClassName,
tkRes,
&ValueCL)))
{
if (ValueCL == GetCl())
pByValueClass = GetHalfBakedClass();
}
} // If field is static typeref
} // If field is self-referencing
} // If 'this' is a value class
// It's not self-referential so try to load it
if (pByValueClass == NULL)
{
// <NOTE>See notes above on why this function is NOTHROW.
// We avoid the call to the loader here because EnC calls InitializeFieldDescs
// from the debugger thread, which is an unmanaged thread (GetThread() returns NULL).
// We can't typically invoke the loader from unmanaged threads.</NOTE>
if (bmtInternal->pModule->IsEditAndContinueEnabled() && GetThread() == NULL)
{
COMPlusThrowHR(E_FAIL);
}
// We load the approximate type of the field to avoid recursion problems.
// MethodTable::DoFullyLoad() will later load it fully
pByValueClass = fsig.GetArgProps().GetTypeHandleThrowing(bmtInternal->pModule,
&bmtGenerics->typeContext,
ClassLoader::LoadTypes,
CLASS_LOAD_APPROXPARENTS,
TRUE
).GetClass();
}
// IF it is an enum, strip it down to its underlying type
if (pByValueClass->IsEnum())
{
BAD_FORMAT_NOTHROW_ASSERT((pByValueClass == GetHalfBakedClass() && bmtEnumMF->dwNumInstanceFields == 1)
|| pByValueClass->GetNumInstanceFields() == 1); // enums must have exactly one field
FieldDesc* enumField = pByValueClass->m_pFieldDescList;
BAD_FORMAT_NOTHROW_ASSERT(!enumField->IsStatic()); // no real static fields on enums
ElementType = enumField->GetFieldType();
BAD_FORMAT_NOTHROW_ASSERT(ElementType != ELEMENT_TYPE_VALUETYPE);
fIsByValue = FALSE; // we're going to treat it as the underlying type now
goto GOT_ELEMENT_TYPE;
}
else if ( (pByValueClass->IsValueClass() == FALSE) &&
(pByValueClass != g_pEnumClass->GetClass()) )
{
BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_MUST_BE_BYVAL, mdTokenNil);
}
// If it is an illegal type, say so
if (pByValueClass->ContainsStackPtr())
{
BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_FIELD, mdTokenNil);
}
// If a class has a field of type ValueType with non-public fields in it,
// the class must "inherit" this characteristic
if (pByValueClass->HasNonPublicFields())
{
SetHasNonPublicFields();
}
#ifdef RVA_FIELD_VALIDATION_ENABLED
if (fHasRVA)
{
dwLog2FieldSize = IsFdStatic(dwMemberAttrs) ? LOG2_PTRSIZE : 0;
break;
}
#endif // RVA_FIELD_VALIDATION_ENABLED
if (IsFdStatic(dwMemberAttrs) == 0)
{
if (pByValueClass->HasFieldsWhichMustBeInited())
SetHasFieldsWhichMustBeInited();
if (pByValueClass->CannotBeBlittedByObjectCloner())
SetCannotBeBlittedByObjectCloner();
}
else
{
// Increment the number of static fields that contain object references.
if (!IsFdHasFieldRVA(dwMemberAttrs))
bmtEnumMF->dwNumStaticBoxedFields++;
}
// Need to create by value class cache. For E&C, this pointer will get
// cached indefinately and not cleaned up as the parent descriptors are
// in the low frequency heap. Use new with the intent of leaking
// this pointer and avoiding the assert .
if (*pByValueClassCache == NULL)
{
*pByValueClassCache = new EEClass * [bmtEnumMF->dwNumInstanceFields + bmtEnumMF->dwNumStaticFields];
memset (*pByValueClassCache, 0, (bmtEnumMF->dwNumInstanceFields + bmtEnumMF->dwNumStaticFields) * sizeof(EEClass **));
}
// Static fields come after instance fields in this list
if (IsFdStatic(dwMemberAttrs))
{
if (!pByValueClass->HasInstantiation())
{
(*pByValueClassCache)[bmtEnumMF->dwNumInstanceFields + dwCurrentStaticField] = pByValueClass;
}
// make sure to record the correct size for static field
// layout
dwLog2FieldSize = LOG2_PTRSIZE; // handle
}
else
{
(*pByValueClassCache)[dwCurrentDeclaredField] = pByValueClass;
dwLog2FieldSize = 0; // unused
}
break;
}
default:
{
BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_FIELD, mdTokenNil);
}
}
// Static fields are not packed
if (IsFdStatic(dwMemberAttrs) && (dwLog2FieldSize < 2))
dwLog2FieldSize = 2;
if (!IsFdStatic(dwMemberAttrs))
{
pFD = &pFieldDescList[dwCurrentDeclaredField];
*totalDeclaredSize += (1 << dwLog2FieldSize);
}
else /* (dwMemberAttrs & mdStatic) */
{
pFD = &pFieldDescList[bmtEnumMF->dwNumInstanceFields + dwCurrentStaticField];
}
bmtMFDescs->ppFieldDescList[i] = pFD;
const LayoutRawFieldInfo *pLayoutFieldInfo;
pLayoutFieldInfo = NULL;
if (HasLayout())
{
const LayoutRawFieldInfo *pwalk = pLayoutRawFieldInfos;
while (pwalk->m_MD != mdFieldDefNil)
{
if (pwalk->m_MD == bmtMetaData->pFields[i])
{
pLayoutFieldInfo = pwalk;
CopyMemory(pNextFieldMarshaler,
&(pwalk->m_FieldMarshaler),
MAXFIELDMARSHALERSIZE);
pNextFieldMarshaler->SetFieldDesc(pFD);
pNextFieldMarshaler->SetExternalOffset(pwalk->m_offset);
((BYTE*&)pNextFieldMarshaler) += MAXFIELDMARSHALERSIZE;
break;
}
pwalk++;
}
}
LPCSTR pszFieldName = NULL;
#ifdef _DEBUG
pszFieldName = pInternalImport->GetNameOfFieldDef(bmtMetaData->pFields[i]);
#endif
// Initialize contents
pFD->Init(
bmtMetaData->pFields[i],
FieldDescElementType,
dwMemberAttrs,
IsFdStatic(dwMemberAttrs),
fHasRVA,
fIsThreadStatic,
fIsContextStatic,
pszFieldName
);
// Check if the ValueType field containing non-publics is overlapped
if(HasExplicitFieldOffsetLayout()
&& pLayoutFieldInfo
&& pLayoutFieldInfo->m_fIsOverlapped
&& pByValueClass
&& pByValueClass->HasNonPublicFields())
{
if (!Security::CanSkipVerification(GetAssembly()->GetDomainAssembly()))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_BADOVERLAP);
}
//SetHasNonVerifiablyOverLayedFields();
}
if (fIsByValue)
{
if (!IsFdStatic(dwMemberAttrs) &&
(IsBlittable() || HasExplicitFieldOffsetLayout()))
{
pFD->m_pMTOfEnclosingClass =
(MethodTable *)(DWORD_PTR)(*pByValueClassCache)[dwCurrentDeclaredField]->GetNumInstanceFieldBytes();
if (pLayoutFieldInfo)
IfFailThrow(pFD->SetOffset(pLayoutFieldInfo->m_offset));
else
pFD->SetOffset(FIELD_OFFSET_VALUE_CLASS);
}
else if (!IsFdStatic(dwMemberAttrs) && IsManagedSequential())
{
pFD->m_pMTOfEnclosingClass =
(MethodTable *)(DWORD_PTR)(*pByValueClassCache)[dwCurrentDeclaredField]->GetNumInstanceFieldBytes();
IfFailThrow(pFD->SetOffset(pLayoutFieldInfo->m_managedOffset));
}
else
{
// static value class fields hold a handle, which is ptr sized
// (instance field layout ignores this value)
pFD->m_pMTOfEnclosingClass = (MethodTable *) LOG2_PTRSIZE;
pFD->SetOffset(FIELD_OFFSET_VALUE_CLASS);
}
}
else
{
// Use the field's MethodTable to temporarily store the field's size
pFD->m_pMTOfEnclosingClass = (MethodTable *)(size_t)dwLog2FieldSize;
// -1 means that this field has not yet been placed
// -2 means that this is a GC Pointer field not yet places
if ((IsBlittable() || HasExplicitFieldOffsetLayout()) && !(IsFdStatic(dwMemberAttrs)))
IfFailThrow(pFD->SetOffset(pLayoutFieldInfo->m_offset));
else if (IsManagedSequential() && !(IsFdStatic(dwMemberAttrs)))
IfFailThrow(pFD->SetOffset(pLayoutFieldInfo->m_managedOffset));
else if (bCurrentFieldIsGCPointer)
pFD->SetOffset(FIELD_OFFSET_UNPLACED_GC_PTR);
else
pFD->SetOffset(FIELD_OFFSET_UNPLACED);
}
if (!IsFdStatic(dwMemberAttrs))
{
if (!fIsByValue)
{
if (++bmtFP->NumInstanceFieldsOfSize[dwLog2FieldSize] == 1)
bmtFP->FirstInstanceFieldOfSize[dwLog2FieldSize] = dwCurrentDeclaredField;
}
dwCurrentDeclaredField++;
if (bCurrentFieldIsGCPointer)
bmtFP->NumInstanceGCPointerFields++;
}
else /* static fields */
{
// Static fields are stored in the vtable after the vtable and interface slots. We don't
// know how large the vtable will be, so we will have to fixup the slot number by
// <vtable + interface size> later.
dwCurrentStaticField++;
if(fHasRVA)
{
#ifdef RVA_FIELD_VALIDATION_ENABLED
// Check if we place ObjectRefs into RVA field
if((FieldDescElementType==ELEMENT_TYPE_CLASS)
||((FieldDescElementType==ELEMENT_TYPE_VALUETYPE)
&&pByValueClass->HasFieldsWhichMustBeInited()))
{
BAD_FORMAT_NOTHROW_ASSERT(!"ObjectRef in an RVA field");
BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_FIELD, mdTokenNil);
}
// Check if we place ValueType with non-public fields into RVA field
if((FieldDescElementType==ELEMENT_TYPE_VALUETYPE)
&&pByValueClass->HasNonPublicFields())
{
GCX_COOP();
if (!Security::CanHaveRVA(pFD, GetAssembly()))
{
BAD_FORMAT_NOTHROW_ASSERT(!"ValueType with non-public fields as a type of an RVA field");
BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_FIELD, mdTokenNil);
}
}
#endif // RVA_FIELD_VALIDATION_ENABLED
// Set the field offset
DWORD rva;
IfFailThrow(pInternalImport->GetFieldRVA(pFD->GetMemberDef(), &rva));
#ifdef RVA_FIELD_VALIDATION_ENABLED
// Ensure that the IL image is loaded. Note that this assembly may
// have an ngen image, but this type may have failed to load during ngen.
pMod->GetFile()->LoadLibrary(FALSE);
DWORD fldSize = (FieldDescElementType == ELEMENT_TYPE_VALUETYPE) ?
pByValueClass->GetNumInstanceFieldBytes() :
GetSizeForCorElementType(FieldDescElementType);
if (!pMod->CheckRvaField(rva, fldSize))
{
GCX_COOP();
if (!Security::CanHaveRVA(pFD, GetAssembly()))
{
BAD_FORMAT_NOTHROW_ASSERT(!"Illegal RVA of a mapped field");
BuildMethodTableThrowException(COR_E_BADIMAGEFORMAT, IDS_CLASSLOAD_BAD_FIELD, mdTokenNil);
}
}
#endif // RVA_FIELD_VALIDATION_ENABLED
IfFailThrow(pFD->SetOffsetRVA(rva));
#ifdef RVA_FIELD_OVERLAPPING_VALIDATION_ENABLED
// Check if the field overlaps with known RVA fields
BYTE* pbModuleBase = pMod->GetILBase();
DWORD dwSizeOfThisField = FieldDescElementType==ELEMENT_TYPE_VALUETYPE ?
pByValueClass->GetNumInstanceFieldBytes() : GetSizeForCorElementType(FieldDescElementType);
BYTE* FDfrom = pbModuleBase + pFD->GetOffset_NoLogging();
BYTE* FDto = FDfrom + dwSizeOfThisField;
ULONG j;
if(g_drRVAField)
{
for(j=1; j < g_ulNumRVAFields; j++)
{
if((*g_drRVAField)[j].pbStart >= FDto) continue;
if((*g_drRVAField)[j].pbEnd <= FDfrom) continue;
}
}
else
{
g_drRVAField = new (nothrow) DynamicArray<RVAFSE>;
if (g_drRVAField == NULL)
return (E_OUTOFMEMORY);
}
(*g_drRVAField)[g_ulNumRVAFields].pbStart = FDfrom;
(*g_drRVAField)[g_ulNumRVAFields].pbEnd = FDto;
g_ulNumRVAFields++;
#endif // RVA_FIELD_OVERLAPPING_VALIDATION_ENABLED
;
}
else if (fIsThreadStatic)
{
DWORD size = 1 << dwLog2FieldSize;
#if defined(ALIGN_ACCESS)
dwThreadStaticsOffset = (DWORD)ALIGN_UP(dwThreadStaticsOffset, size);
#endif
IfFailThrow(pFD->SetOffset(dwThreadStaticsOffset)); // offset is the bucket index
dwThreadStaticsOffset += size;
}
else if (fIsContextStatic)
{
DWORD size = 1 << dwLog2FieldSize;
#if defined(ALIGN_ACCESS)
dwContextStaticsOffset = (DWORD)ALIGN_UP(dwContextStaticsOffset, size);
#endif
IfFailThrow(pFD->SetOffset(dwContextStaticsOffset)); // offset is the bucket index
dwContextStaticsOffset += size;
}
else
{
bmtFP->NumStaticFieldsOfSize[dwLog2FieldSize]++;
if (bCurrentFieldIsGCPointer)
bmtFP->NumStaticGCPointerFields++;
if (fIsByValue)
bmtFP->NumStaticGCBoxedFields++;
}
}
}
EEClass *pParent = NULL;
if (bmtParent)
pParent = (bmtParent->pParentMethodTable) ? bmtParent->pParentMethodTable->GetClass() : NULL;
else
{
pParent = GetParentClass();
}
DWORD dwNumInstanceFields = dwCurrentDeclaredField + (pParent ? pParent->m_wNumInstanceFields : 0);
DWORD dwNumStaticFields = bmtEnumMF->dwNumStaticFields;
if (dwNumInstanceFields != (WORD)dwNumInstanceFields || dwNumStaticFields != (WORD)dwNumStaticFields)
{
BuildMethodTableThrowException(IDS_EE_TOOMANYFIELDS);
}
GetHalfBakedClass()->SetNumInstanceFields((WORD)dwNumInstanceFields);
GetHalfBakedClass()->SetNumStaticFields((WORD)dwNumStaticFields);
if (ShouldAlign8(dwR8Fields, dwNumInstanceFields))
{
SetAlign8Candidate();
}
if (pbmtTCSInfo)
{
pbmtTCSInfo->dwThreadStaticsSize = dwThreadStaticsOffset;
pbmtTCSInfo->dwContextStaticsSize = dwContextStaticsOffset;
}
//========================================================================
// END:
// Go thru all fields and initialize their FieldDescs.
//========================================================================
return;
}
//*******************************************************************************
BOOL MethodTableBuilder::TestOverrideForAccessibility(Assembly *pParentAssembly,
Assembly *pChildAssembly,
DWORD dwParentAttrs)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
} CONTRACTL_END;
BOOL isSameAssembly = (pChildAssembly == pParentAssembly);
// AKA "strict bit". This means that overridability is tightly bound to accessibility.
if (IsMdCheckAccessOnOverride(dwParentAttrs))
{
// Same Assembly
if (isSameAssembly || pParentAssembly->GrantsFriendAccessTo(pChildAssembly))
{
// We are not allowed to override private members
if ((dwParentAttrs & mdMemberAccessMask) <= mdPrivate)
{
return FALSE;
}
}
// Cross-Assembly
else
{
// If the method marks itself as check visibility the the method must be
// public, FamORAssem, or family
if((dwParentAttrs & mdMemberAccessMask) <= mdAssem)
{
return FALSE;
}
}
}
return TRUE;
}
//*******************************************************************************
VOID MethodTableBuilder::TestOverRide(DWORD dwParentAttrs,
DWORD dwMemberAttrs,
Module *pModule,
Module *pParentModule,
mdToken method)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM());
PRECONDITION(IsMdVirtual(dwParentAttrs));
PRECONDITION(IsMdVirtual(dwMemberAttrs));
} CONTRACTL_END;
Assembly *pAssembly = pModule->GetAssembly();
Assembly *pParentAssembly = pParentModule->GetAssembly();
BOOL isSameModule = (pModule == pParentModule);
BOOL isSameAssembly = (pAssembly == pParentAssembly);
// Virtual methods cannot be static
if (IsMdStatic(dwMemberAttrs))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_STATICVIRTUAL, method);
}
if (!TestOverrideForAccessibility(pParentAssembly, pAssembly, dwParentAttrs))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_ACCESS_FAILURE, method);
}
//
// Refer to Partition II, 9.3.3 for more information on what is permitted.
//
enum WIDENING_STATUS
{
e_NO, // NO
e_YES, // YES
e_SA, // YES, but only when same assembly
e_NSA, // YES, but only when NOT same assembly
e_SM, // YES, but only when same module
};
C_ASSERT(mdPrivateScope == 0x00);
C_ASSERT(mdPrivate == 0x01);
C_ASSERT(mdFamANDAssem == 0x02);
C_ASSERT(mdAssem == 0x03);
C_ASSERT(mdFamily == 0x04);
C_ASSERT(mdFamORAssem == 0x05);
C_ASSERT(mdPublic == 0x06);
static const DWORD dwCount = mdPublic - mdPrivateScope + 1;
static const WIDENING_STATUS rgWideningTable[dwCount][dwCount] =
// | Base type
// Subtype | mdPrivateScope mdPrivate mdFamANDAssem mdAssem mdFamily mdFamORAssem mdPublic
// --------------+-------------------------------------------------------------------------------------------------------
/*mdPrivateScope | */ { { e_SM, e_NO, e_NO, e_NO, e_NO, e_NO, e_NO },
/*mdPrivate | */ { e_SM, e_YES, e_NO, e_NO, e_NO, e_NO, e_NO },
/*mdFamANDAssem | */ { e_SM, e_YES, e_SA, e_NO, e_NO, e_NO, e_NO },
/*mdAssem | */ { e_SM, e_YES, e_SA, e_SA, e_NO, e_NO, e_NO },
/*mdFamily | */ { e_SM, e_YES, e_YES, e_NO, e_YES, e_NSA, e_NO },
/*mdFamORAssem | */ { e_SM, e_YES, e_YES, e_SA, e_YES, e_YES, e_NO },
/*mdPublic | */ { e_SM, e_YES, e_YES, e_YES, e_YES, e_YES, e_YES } };
DWORD idxParent = (dwParentAttrs & mdMemberAccessMask) - mdPrivateScope;
DWORD idxMember = (dwMemberAttrs & mdMemberAccessMask) - mdPrivateScope;
CONSISTENCY_CHECK(idxParent < dwCount);
CONSISTENCY_CHECK(idxMember < dwCount);
WIDENING_STATUS entry = rgWideningTable[idxMember][idxParent];
if (entry == e_NO ||
(entry == e_SA && !isSameAssembly && !pParentAssembly->GrantsFriendAccessTo(pAssembly)) ||
(entry == e_NSA && isSameAssembly) ||
(entry == e_SM && !isSameModule)
)
{
BuildMethodTableThrowException(IDS_CLASSLOAD_REDUCEACCESS, method);
}
return;
}
//*******************************************************************************
VOID MethodTableBuilder::TestMethodImpl(Module *pDeclModule,
Module *pImplModule,
mdToken tokDecl,
mdToken tokImpl)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM());
PRECONDITION(TypeFromToken(tokDecl) == mdtMethodDef);
PRECONDITION(TypeFromToken(tokImpl) == mdtMethodDef);
PRECONDITION(CheckPointer(pDeclModule));
PRECONDITION(CheckPointer(pImplModule));
}
CONTRACTL_END
BOOL isSameModule = pDeclModule->Equals(pImplModule);
Assembly *pDeclAssembly = pDeclModule->GetAssembly();
Assembly *pImplAssembly = pImplModule->GetAssembly();
IMDInternalImport *pIMDDecl = pDeclModule->GetMDImport();
IMDInternalImport *pIMDImpl = pImplModule->GetMDImport();
DWORD dwDeclAttrs = pIMDDecl->GetMethodDefProps(tokDecl);
DWORD dwImplAttrs = pIMDImpl->GetMethodDefProps(tokImpl);
HRESULT hr = COR_E_TYPELOAD;
if (!IsMdVirtual(dwDeclAttrs))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_NONVIRTUAL_DECL);
}
if (!IsMdVirtual(dwImplAttrs))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL);
}
// Virtual methods cannot be static
if (IsMdStatic(dwDeclAttrs))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_STATICVIRTUAL);
}
if (IsMdStatic(dwImplAttrs))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_STATICVIRTUAL);
}
if (IsMdFinal(dwDeclAttrs))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_FINAL_DECL);
}
// Since MethodImpl's do not affect the visibility of the Decl method, there's
// no need to check.
// If Decl's parent is other than this class, Decl must not be private
mdTypeDef tkImplParent = mdTypeDefNil;
mdTypeDef tkDeclParent = mdTypeDefNil;
if (FAILED(hr = pIMDDecl->GetParentToken(tokDecl, &tkDeclParent)))
{
BuildMethodTableThrowException(hr, *bmtError);
}
if (FAILED(hr = pIMDImpl->GetParentToken(tokImpl, &tkImplParent)))
{
BuildMethodTableThrowException(hr, *bmtError);
}
// Make sure that we test for accessibility restrictions only if the decl is
// not within our own type, as we are allowed to methodImpl a private with the
// strict bit set if it is in our own type.
if (!isSameModule || tkDeclParent != tkImplParent)
{
if (!TestOverrideForAccessibility(pDeclAssembly, pImplAssembly, dwDeclAttrs))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_ACCESS_FAILURE, tokImpl);
}
// Decl's parent must not be tdSealed
mdToken tkGrandParentDummyVar;
DWORD dwDeclTypeAttrs;
pIMDDecl->GetTypeDefProps(tkDeclParent, &dwDeclTypeAttrs, &tkGrandParentDummyVar);
if (IsTdSealed(dwDeclTypeAttrs))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_SEALED_DECL);
}
}
return;
}
#if defined(_DEBUG) && !defined(STUB_DISPATCH_ALL)
//*******************************************************************************
//
// If a derived class is being created, and it does not override a virtual method
// from the parent class, it will use the same MethodDesc prestub as the parent.
// This functions tracks such inherited methods.
//
// Note that the tracking does not completely reflect the class hierarchy - if the
// method has already been jitted, the deriving class will use the jitted code directly
// instead of using the prestub
//
void MethodTableBuilder::MarkInheritedVirtualMethods(MethodTable *childMT, MethodTable * parentMT)
{
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
FORBID_FAULT;
PRECONDITION(CheckPointer(parentMT));
}
CONTRACTL_END
CONSISTENCY_CHECK(CheckPointer(parentMT));
unsigned parentVirtMethods = parentMT->GetNumVirtuals();
// Walk all the methods in the parent's vtable
for (unsigned i = 0; i < parentVirtMethods; i++)
{
// Does the childVtable use the same
if (childMT->GetSlot(i) == parentMT->GetSlot(i))
{
MethodDesc* pMD = childMT->GetUnknownMethodDescForSlot(i);
// The permitted prestub calls count is persisted in the ngen image.
// We have to simulate the increments as if the method got jitted.
// This is necesary since we are running code during ngen.
pMD->IncPermittedPrestubCalls();
if (!DoesSlotCallPrestub((BYTE *)childMT->GetSlot(i)))
pMD->IncPrestubCalls();
}
}
}
#endif // defined(_DEBUG) && !defined(STUB_DISPATCH_ALL)
//*******************************************************************************
void MethodTableBuilder::SetSecurityFlagsOnMethod(MethodDesc* pParentMethodDesc,
MethodDesc* pNewMD,
mdToken tokMethod,
DWORD dwMemberAttrs,
bmtInternalInfo* bmtInternal,
bmtMetaDataInfo* bmtMetaData)
{
if (!Security::IsSecurityOn())
return;
DWORD dwMethDeclFlags = 0;
DWORD dwMethNullDeclFlags = 0;
DWORD dwClassDeclFlags = 0xffffffff;
DWORD dwClassNullDeclFlags = 0xffffffff;
if ( IsMdHasSecurity(dwMemberAttrs) || IsTdHasSecurity(GetAttrClass()) || pNewMD->IsNDirect() )
{
// Disable inlining for any function which does runtime declarative
// security actions.
DWORD dwRuntimeSecurityFlags = (pNewMD->GetSecurityFlagsDuringClassLoad(bmtInternal->pInternalImport,
tokMethod,
GetCl(),
&dwClassDeclFlags,
&dwClassNullDeclFlags,
&dwMethDeclFlags,
&dwMethNullDeclFlags) & DECLSEC_RUNTIME_ACTIONS);
if (dwRuntimeSecurityFlags)
{
// If we get here it means
// - We have some "runtime" actions on this method. We dont care about "linktime" demands
// - If this is a pinvoke method, then the unmanaged code access demand has not been suppressed
pNewMD->SetNotInline(true);
pNewMD->SetInterceptedForDeclSecurity(true);
pNewMD->SetInterceptedForDeclSecurityCASDemandsOnly(
MethodSecurityDescriptor::IsDeclSecurityCASDemandsOnly(dwRuntimeSecurityFlags, tokMethod, bmtInternal->pInternalImport ));
}
}
if ( IsMdHasSecurity(dwMemberAttrs) )
{
// We only care about checks that are not empty...
dwMethDeclFlags &= ~dwMethNullDeclFlags;
if ( dwMethDeclFlags & (DECLSEC_LINK_CHECKS|DECLSEC_NONCAS_LINK_DEMANDS) )
{
pNewMD->SetRequiresLinktimeCheck();
}
if ( dwMethDeclFlags & (DECLSEC_INHERIT_CHECKS|DECLSEC_NONCAS_INHERITANCE) )
{
pNewMD->SetRequiresInheritanceCheck();
if (IsInterface())
{
GetHalfBakedClass()->SetSomeMethodsRequireInheritanceCheck();
}
}
}
// Linktime checks on a method override those on a class.
// If the method has an empty set of linktime checks,
// then don't require linktime checking for this method.
if ( RequiresLinktimeCheck() && !(dwMethNullDeclFlags & DECLSEC_LINK_CHECKS) )
{
pNewMD->SetRequiresLinktimeCheck();
}
if ( pParentMethodDesc != NULL &&
(pParentMethodDesc->RequiresInheritanceCheck() ||
pParentMethodDesc->ParentRequiresInheritanceCheck()) )
{
pNewMD->SetParentRequiresInheritanceCheck();
}
// Methods on an interface that includes an UnmanagedCode check
// suppression attribute are assumed to be interop methods. We ask
// for linktime checks on these.
// Also place linktime checks on all P/Invoke calls.
if ((IsInterface() &&
(bmtInternal->pInternalImport->GetCustomAttributeByName(GetCl(),
COR_SUPPRESS_UNMANAGED_CODE_CHECK_ATTRIBUTE_ANSI,
NULL,
NULL) == S_OK ||
bmtInternal->pInternalImport->GetCustomAttributeByName(pNewMD->GetMemberDef(),
COR_SUPPRESS_UNMANAGED_CODE_CHECK_ATTRIBUTE_ANSI,
NULL,
NULL) == S_OK) ) ||
pNewMD->IsNDirect() ||
(pNewMD->IsComPlusCall() && !IsInterface()))
{
pNewMD->SetRequiresLinktimeCheck();
}
// All public methods on public types will do a link demand of
// full trust, unless AllowUntrustedCaller attribute is set
if (
#ifdef _DEBUG
g_pConfig->Do_AllowUntrustedCaller_Checks() &&
#endif
!pNewMD->RequiresLinktimeCheck())
{
// If the method is public (visible outside it's assembly),
// and the type is public and the assembly
// is not marked with AllowUntrustedCaller attribute, do
// a link demand for full trust on all callers note that
// this won't be effective on virtual overrides. The caller
// can allways do a virtual call on the base type / interface
if (Security::MethodIsVisibleOutsideItsAssembly(
dwMemberAttrs, GetAttrClass()))
{
_ASSERTE(GetClassLoader());
_ASSERTE(GetAssembly());
// See if the Assembly has AllowUntrustedCallerChecks CA
// Pull this page in last
if (!GetAssembly()->AllowUntrustedCaller())
pNewMD->SetRequiresLinktimeCheck();
}
}
// If it's a delegate BeginInvoke, we need to do a HostProtection check for synchronization
if(IsAnyDelegateClass())
{
DelegateEEClass* pDelegateClass = (DelegateEEClass*)GetHalfBakedClass();
if(pNewMD == pDelegateClass->m_pBeginInvokeMethod)
pNewMD->SetRequiresLinktimeCheck();
}
}
//*******************************************************************************
//
// Used by BuildMethodTable
//
// Determine vtable placement for each member in this class
//
VOID MethodTableBuilder::PlaceMembers(DWORD numDeclaredInterfaces,
BuildingInterfaceInfo_t *pBuildingInterfaceList,
AllocMemTracker *pamTracker)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
PRECONDITION(CheckPointer(this));
PRECONDITION(CheckPointer(bmtInternal));
PRECONDITION(CheckPointer(bmtMetaData));
PRECONDITION(CheckPointer(bmtError));
PRECONDITION(CheckPointer(bmtProp));
PRECONDITION(CheckPointer(bmtInterface));
PRECONDITION(CheckPointer(bmtParent));
PRECONDITION(CheckPointer(bmtMFDescs));
PRECONDITION(CheckPointer(bmtEnumMF));
PRECONDITION(CheckPointer(bmtMethodImpl));
PRECONDITION(CheckPointer(bmtVT));
}
CONTRACTL_END;
#ifdef _DEBUG
LPCUTF8 pszDebugName,pszDebugNamespace;
bmtInternal->pModule->GetMDImport()->GetNameOfTypeDef(GetCl(), &pszDebugName, &pszDebugNamespace);
#endif // _DEBUG
HRESULT hr = S_OK;
bmtVT->wCCtorSlot = MethodTable::NO_SLOT;
bmtVT->wDefaultCtorSlot = MethodTable::NO_SLOT;
DeclaredMethodIterator it(*this);
while (it.Next())
{
PCCOR_SIGNATURE pMemberSignature = NULL;
DWORD cMemberSignature = 0;
DWORD dwParentAttrs;
// for IL code that is implemented here must have a valid code RVA
// this came up due to a linker bug where the ImplFlags/DescrOffset were
// being set to null and we weren't coping with it
if (it.RVA() == 0)
{
if((it.ImplFlags() == 0 || IsMiIL(it.ImplFlags()) || IsMiOPTIL(it.ImplFlags())) &&
!IsMiRuntime(it.ImplFlags()) &&
!IsMdAbstract(it.Attrs()) &&
!IsReallyMdPinvokeImpl(it.Attrs()) &&
!IsMiInternalCall(it.ImplFlags()) &&
!(bmtInternal->pModule)->IsReflection() &&
!(IsInterface() && !IsMdStatic(it.Attrs())) &&
bmtDomain->IsExecutable())
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MISSINGMETHODRVA, it.Token());
}
}
else
{
if (!GetModule()->CheckIL(it.RVA()))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MISSINGMETHODRVA, it.Token());
}
}
// If this member is a method which overrides a parent method, it will be set to non-NULL
MethodDesc *pParentMethodDesc = NULL;
BOOL fIsInitMethod = FALSE;
BOOL fIsCCtor = FALSE;
BOOL fIsDefaultCtor = FALSE;
#ifdef _DEBUG
if(GetHalfBakedClass()->m_fDebuggingClass && g_pConfig->ShouldBreakOnMethod(it.Name()))
_ASSERTE(!"BreakOnMethodName");
#endif // _DEBUG
// constructors and class initialisers are special
if (IsMdRTSpecialName(it.Attrs()))
{
if (IsMdStatic(it.Attrs()))
{
// The only rtSpecialName static method allowed is the .cctor
if(strcmp(it.Name(), COR_CCTOR_METHOD_NAME) != 0)
{
BuildMethodTableThrowException(IDS_CLASSLOAD_GENERAL);
}
else
{
// Validate that we have the correct signature for the .cctor
pMemberSignature = it.GetSig(&cMemberSignature);
PCCOR_SIGNATURE pbBinarySig;
ULONG cbBinarySig;
// .cctor must return void, have default call conv, and have no args
unsigned cconv,nargs;
pbBinarySig = pMemberSignature;
cconv = CorSigUncompressData(pbBinarySig);
nargs = CorSigUncompressData(pbBinarySig);
if((*pbBinarySig != ELEMENT_TYPE_VOID)||(nargs!=0)||(cconv != IMAGE_CEE_CS_CALLCONV_DEFAULT))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_GENERAL);
}
else
{
gsig_SM_RetVoid.GetBinarySig(&pbBinarySig, &cbBinarySig);
// No substitutions for type parameters as the method is static
if (MetaSig::CompareMethodSigs(pbBinarySig, cbBinarySig,
SystemDomain::SystemModule(), NULL,
pMemberSignature, cMemberSignature, bmtInternal->pModule, NULL))
{
fIsCCtor = TRUE;
}
else
{
BuildMethodTableThrowException(IDS_CLASSLOAD_GENERAL);
}
}
}
}
else
{
// Verify the name for a constructor.
if(strcmp(it.Name(), COR_CTOR_METHOD_NAME) != 0)
{
BuildMethodTableThrowException(IDS_CLASSLOAD_GENERAL);
}
else
{
// See if this is a default constructor. If so, remember it for later.
pMemberSignature = it.GetSig(&cMemberSignature);
PCCOR_SIGNATURE pbBinarySig;
ULONG cbBinarySig;
// .ctor must return void
pbBinarySig = pMemberSignature;
CorSigUncompressData(pbBinarySig); // get call conv out of the way
CorSigUncompressData(pbBinarySig); // get num args out of the way
if(*pbBinarySig != ELEMENT_TYPE_VOID)
{
BuildMethodTableThrowException(IDS_CLASSLOAD_GENERAL);
}
else
{
gsig_IM_RetVoid.GetBinarySig(&pbBinarySig, &cbBinarySig);
if (MetaSig::CompareMethodSigs(pbBinarySig, cbBinarySig,
SystemDomain::SystemModule(), NULL,
pMemberSignature, cMemberSignature, bmtInternal->pModule, NULL))
fIsDefaultCtor = TRUE;
}
fIsInitMethod = TRUE;
}
}
}
// The method does not have the special marking
else
{
if (IsMdVirtual(it.Attrs()))
{
// 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(it.Name());
BOOL fMethodConstraintsMatch = FALSE;
// If the member is marked with a new slot we do not need to find it
// in the parent
if (!IsMdNewSlot(it.Attrs()))
{
// If we're not doing sanity checks, then assume that any method declared static
// does not attempt to override some virtual parent.
if (!IsMdStatic(it.Attrs()) && 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
{
HRESULT hrTmp;
hrTmp = LoaderFindMethodInClass(
it.Name(),
bmtInternal->pModule,
it.Token(),
&pParentMethodDesc,
&pMemberSignature, &cMemberSignature,
dwHashName, &fMethodConstraintsMatch);
if (FAILED(hrTmp))
{
BuildMethodTableThrowException(hrTmp, *bmtError);
}
}
if (pParentMethodDesc != NULL)
{
dwParentAttrs = pParentMethodDesc->GetAttrs();
if (!IsMdVirtual(dwParentAttrs))
{
BuildMethodTableThrowException(BFA_NONVIRT_NO_SEARCH, it.Token());
}
CONSISTENCY_CHECK(!fIsInitMethod);
// if we end up pointing at a slot that is final we are not allowed to override it.
if(IsMdFinal(dwParentAttrs))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_FINAL_DECL, it.Token());
}
else if(!bmtProp->fNoSanityChecks)
{
TestOverRide(dwParentAttrs, it.Attrs(), GetModule(), pParentMethodDesc->GetModule(), it.Token());
}
if (!fMethodConstraintsMatch)
{
BuildMethodTableThrowException(
IDS_CLASSLOAD_CONSTRAINT_MISMATCH_ON_IMPLICIT_OVERRIDE,
it.Token());
}
if (g_pConfig->ShouldRejectSafeHandleFinalizers() &&
bmtParent->pParentMethodTable->HasCriticalFinalizer() && 0 == strcmp("Finalize", it.Name()))
{
bool isSafeHandle = false;
// Is this a subclass of SafeHandle?
MethodTable * currMT = bmtParent->pParentMethodTable;
while(currMT != NULL)
{
if (currMT == g_Mscorlib.FetchClass(CLASS__SAFE_HANDLE))
{
isSafeHandle = true;
break;
}
currMT = currMT->GetParentMethodTable();
}
if (isSafeHandle)
{
BuildMethodTableThrowException(IDS_CLASSLOAD_SH_SUBCLASS_FINALIZER, it.Token());
}
}
}
}
}
}
}
// Now we know the classification we can allocate the correct type of
// method desc and perform any classification specific initialization.
bmtTokenRangeNode *pTR = GetTokenRange(it.Token(),
&(bmtMetaData->ranges[it.MethodType()][it.MethodImpl()])); // throws
CONSISTENCY_CHECK(pTR->cMethods != 0);
bmtMethodDescSet *set = &bmtMFDescs->sets[it.MethodType()][it.MethodImpl()];
CONSISTENCY_CHECK(CheckPointer(set));
MethodDescChunk *pChunk = set->pChunkList[pTR->dwCurrentChunk];
// The MethodDesc we allocate for this method
MethodDesc *pNewMD = pChunk->GetMethodDescAt(pTR->dwCurrentIndex);
LPCSTR pName = it.Name();
if (pName == NULL)
pName = bmtInternal->pInternalImport->GetNameOfMethodDef(it.Token());
// Update counters to prepare for next method desc allocation.
pTR->dwCurrentIndex++;
if (pTR->dwCurrentIndex == MethodDescChunk::GetMaxMethodDescs(it.Classification()))
{
pTR->dwCurrentChunk++;
pTR->dwCurrentIndex = 0;
}
#ifdef _DEBUG
LPCUTF8 pszDebugMethodName = bmtInternal->pInternalImport->GetNameOfMethodDef(it.Token());
size_t len = strlen(pszDebugMethodName) + 1;
LPCUTF8 pszDebugMethodNameCopy = (char*) pamTracker->Track(bmtDomain->GetLowFrequencyHeap()->AllocMem(len));
strcpy_s((char *) pszDebugMethodNameCopy, len, pszDebugMethodName);
#endif // _DEBUG
// Do the init specific to each classification of MethodDesc & assign some common fields
InitMethodDesc(bmtDomain,
pNewMD,
it.Classification(),
it.Token(),
it.ImplFlags(),
it.Attrs(),
FALSE,
it.RVA(),
bmtInternal->pInternalImport,
pName,
#ifdef _DEBUG
pszDebugMethodNameCopy,
GetDebugClassName(),
"", // FIX this happens on global methods, give better info
#endif // _DEBUG
pamTracker
);
CONSISTENCY_CHECK(CheckPointer(bmtParent->ppParentMethodDescBufPtr));
CONSISTENCY_CHECK(((bmtParent->ppParentMethodDescBufPtr - bmtParent->ppParentMethodDescBuf)
/ sizeof(MethodDesc*)) < NumDeclaredMethods());
*(bmtParent->ppParentMethodDescBufPtr++) = pParentMethodDesc;
*(bmtParent->ppParentMethodDescBufPtr++) = pNewMD;
// Declarative Security
SetSecurityFlagsOnMethod(pParentMethodDesc, pNewMD, it.Token(), it.Attrs(), bmtInternal, bmtMetaData);
it.SetMethodDesc(pNewMD);
it.SetParentMethodDesc(pParentMethodDesc);
// Make sure that fcalls have a 0 rva. This is assumed by the prejit fixup logic
if ((it.Classification() & ~mdcMethodImpl) == mcFCall && it.RVA() != 0)
{
BuildMethodTableThrowException(BFA_ECALLS_MUST_HAVE_ZERO_RVA, it.Token());
}
if (!IsMdVirtual(it.Attrs()))
{
// non-vtable method
CONSISTENCY_CHECK(bmtVT->pNonVtableMD[bmtVT->dwCurrentNonVtableSlot] == NULL);
bmtVT->pNonVtableMD[bmtVT->dwCurrentNonVtableSlot] = pNewMD; // Not prestub addr
pNewMD->SetSlot((WORD) bmtVT->dwCurrentNonVtableSlot);
if (fIsDefaultCtor)
bmtVT->wDefaultCtorSlot = (WORD) bmtVT->dwCurrentNonVtableSlot;
else if (fIsCCtor)
bmtVT->wCCtorSlot = (WORD) bmtVT->dwCurrentNonVtableSlot;
bmtVT->dwCurrentNonVtableSlot++;
}
else
{
pNewMD->SetSlot(MethodTable::NO_SLOT); // mark it initially as unplaced
// vtable method
if (IsInterface())
{
CONSISTENCY_CHECK(pParentMethodDesc == NULL);
// if we're an interface, our slot number is fixed
CONSISTENCY_CHECK(bmtVT->GetMethodDescForSlot(bmtVT->dwCurrentVtableSlot) == NULL);
bmtVT->SetMethodDescForSlot(bmtVT->dwCurrentVtableSlot, pNewMD);
pNewMD->SetSlot((WORD) bmtVT->dwCurrentVtableSlot);
bmtVT->dwCurrentVtableSlot++;
}
else if (pParentMethodDesc != NULL)
{
WORD slotNumber = pParentMethodDesc->GetSlot();
// No need for placeholder MDs with stub dispatch.
CONSISTENCY_CHECK(!pParentMethodDesc->IsInterface());
// we are overriding a parent method, so place this method now
bmtVT->SetMethodDescForSlot(slotNumber, pNewMD);
pNewMD->SetSlot(slotNumber);
CONSISTENCY_CHECK(!bmtVT->pDispatchMapBuilder->Contains(
DispatchMapTypeID::ThisClassID(), pParentMethodDesc->GetSlot()));
CONSISTENCY_CHECK(bmtParent->pParentMethodTable == NULL || slotNumber < bmtParent->pParentMethodTable->GetNumVirtuals());
// We add the override entry to the mapping.
bmtVT->pDispatchMapBuilder->InsertMDMapping(
DispatchMapTypeID::ThisClassID(),
pParentMethodDesc->GetSlot(),
pNewMD,
FALSE);
}
else
{
bmtVT->SetMethodDescForSlot(bmtVT->dwCurrentVtableSlot, pNewMD);
pNewMD->SetSlot((WORD) bmtVT->dwCurrentVtableSlot);
bmtVT->dwCurrentVtableSlot++;
}
}
// 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.
if(it.Classification() & mdcMethodImpl) {
for(DWORD m = 0; m < bmtEnumMF->dwNumberMethodImpls; m++) {
if(it.Token() == bmtMetaData->rgMethodImplTokens[m].methodBody) {
MethodDesc* pDeclMD = NULL;
BOOL fIsMethod;
mdToken mdDecl = bmtMetaData->rgMethodImplTokens[m].methodDecl;
DWORD dwDeclAttrs;
Substitution *pDeclSubst = &bmtMetaData->pMethodDeclSubsts[m];
// Get the parent
mdToken tkParent = mdTypeDefNil;
if (TypeFromToken(mdDecl) == mdtMethodDef || TypeFromToken(mdDecl) == mdtMemberRef)
{
if (FAILED(hr = bmtInternal->pInternalImport->GetParentToken(mdDecl,&tkParent)))
{
BuildMethodTableThrowException(hr, *bmtError);
}
}
// The DECL has been declared within the class
// that we're currently building.
if (GetCl() == tkParent)
{
hr = S_OK;
if(pThrowableAvailable(bmtError->pThrowable))
*(bmtError->pThrowable) = NULL;
if(TypeFromToken(mdDecl) != mdtMethodDef)
{
Module* pModule;
if (FAILED(hr = FindMethodDeclarationForMethodImpl(
mdDecl, &mdDecl, FALSE, &pModule)))
{
BuildMethodTableThrowException(hr, *bmtError);
}
// Remember, the decl is in the same type as the impl.
CONSISTENCY_CHECK(pModule == bmtInternal->pModule);
}
dwDeclAttrs = bmtInternal->pInternalImport->GetMethodDefProps(mdDecl);
}
else
{
pDeclMD = (MethodDesc*) MemberLoader::GetDescFromMemberDefOrRefThrowing(
bmtInternal->pModule,
mdDecl,
&fIsMethod,
&bmtGenerics->typeContext, // type context
FALSE, // don't demand generic method args
0,
NULL,
FALSE /*allowInstParam*/,
CLASS_LOAD_APPROXPARENTS);
_ASSERTE(pDeclMD != NULL);
// We found a non-method, so throw.
if (!fIsMethod)
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_DECLARATIONNOTFOUND, it.Token());
}
mdDecl = mdTokenNil;
dwDeclAttrs = pDeclMD->GetAttrs();
}
// Make sure the impl and decl are virtaul
if (!IsMdVirtual(dwDeclAttrs))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL, it.Token());
}
if (!IsMdVirtual(it.Attrs()))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_VIRTUALMISMATCH, it.Token());
}
bmtMethodImpl->AddMethod(pNewMD,
pDeclMD,
mdDecl,
pDeclSubst);
}
}
}
// check for proper use of the Managed and native flags
if (IsMiManaged(it.ImplFlags()))
{
if (IsMiIL(it.ImplFlags()) || IsMiRuntime(it.ImplFlags())) // IsMiOPTIL(it.ImplFlags()) no longer supported
{
// No need to set code address, pre stub used automatically.
}
else
{
if (IsMiNative(it.ImplFlags()))
{
// For now simply disallow managed native code if you turn this on you have to at least
// insure that we have SkipVerificationPermission or equivalent
BuildMethodTableThrowException(BFA_MANAGED_NATIVE_NYI, it.Token());
}
else
{
BuildMethodTableThrowException(BFA_BAD_IMPL_FLAGS, it.Token());
}
}
}
else
{
if (IsMiNative(it.ImplFlags()) && IsGlobalClass())
{
// global function unmanaged entrypoint via IJW thunk was handled
// above.
}
else
{
BuildMethodTableThrowException(IDS_CLASSLOAD_BAD_UNMANAGED_RVA, it.Token());
}
if (it.Classification() != mcNDirect)
{
BuildMethodTableThrowException(BFA_BAD_UNMANAGED_ENTRY_POINT);
}
}
// Turn off inlining for any calls
// that are marked in the metadata as not being inlineable.
if(IsMiNoInlining(it.ImplFlags()))
{
pNewMD->SetNotInline(true);
}
// Vararg methods are not allowed inside generic classes
// and nor can they be generic methods.
if (bmtGenerics->GetNumGenericArgs() > 0 || ((it.Classification() & mdcClassification) == mcInstantiated) )
{
// We've been trying to avoid asking for the signature - now we need it
if (pMemberSignature == NULL)
{
pMemberSignature = it.GetSig(&cMemberSignature);
}
if (MetaSig::IsVarArg(GetModule(), pMemberSignature))
{
BuildMethodTableThrowException(BFA_GENCODE_NOT_BE_VARARG);
}
}
} /* end ... for each member */
}
//*******************************************************************************
// InitMethodDesc takes a pointer to space that's already allocated for the
// particular type of MethodDesc, and initializes based on the other info.
// This factors logic between PlaceMembers (the regular code path) & AddMethod
// (Edit & Continue (EnC) code path) so we don't have to maintain separate copies.
VOID MethodTableBuilder::InitMethodDesc(BaseDomain *bmtDomain,
MethodDesc *pNewMD, // This is should actually be of the correct
// sub-type, based on Classification
DWORD Classification,
mdToken tok,
DWORD dwImplFlags,
DWORD dwMemberAttrs,
BOOL fEnC,
DWORD RVA, // Only needed for NDirect case
IMDInternalImport *pIMDII, // Needed for NDirect, EEImpl(Delegate) cases
LPCSTR pMethodName, // Only needed for mcEEImpl (Delegate) case
#ifdef _DEBUG
LPCUTF8 pszDebugMethodName,
LPCUTF8 pszDebugClassName,
LPUTF8 pszDebugMethodSignature,
#endif // _DEBUG
AllocMemTracker *pamTracker
)
{
CONTRACTL
{
THROWS;
WRAPPER(GC_TRIGGERS);
INJECT_FAULT(COMPlusThrowOM());
}
CONTRACTL_END
LOG((LF_CORDB, LL_EVERYTHING, "EEC::IMD: pNewMD:0x%x for tok:0x%x (%s::%s)\n",
pNewMD, tok, pszDebugClassName, pszDebugMethodName));
// Now we know the classification we can perform any classification specific initialization.
// The method desc is zero inited by the caller.
switch (Classification & mdcClassification)
{
case mcNDirect:
{
// NDirect specific initialization.
NDirectMethodDesc *pNewNMD = (NDirectMethodDesc*)pNewMD;
// Allocate writeable data
pNewNMD->ndirect.m_pWriteableData = (NDirectWriteableData*)
pamTracker->Track(bmtDomain->GetHighFrequencyHeap()->AllocMem(sizeof(NDirectWriteableData)));
#ifdef HAS_NDIRECT_IMPORT_PRECODE
pNewNMD->ndirect.m_pImportThunkGlue = Precode::Allocate(PRECODE_NDIRECT_IMPORT, pNewMD, FALSE,
bmtDomain, pamTracker)->AsNDirectImportPrecode();
#else
pNewNMD->GetNDirectImportThunkGlue()->Init(pNewNMD, bmtDomain);
#endif
pNewNMD->GetWriteableData()->m_pNDirectTarget = pNewNMD->GetNDirectImportThunkGlue()->GetEntrypoint();
}
break;
case mcFCall:
break;
case mcEEImpl:
// For the Invoke method we will set a standard invoke method.
BAD_FORMAT_NOTHROW_ASSERT(IsAnyDelegateClass());
// For the asserts, either the pointer is NULL (since the class hasn't
// been constructed yet), or we're in EnC mode, meaning that the class
// does exist, but we may be re-assigning the field to point to an
// updated MethodDesc
// It is not allowed for EnC to replace one of the runtime builtin methods
if (strcmp(pMethodName, "Invoke") == 0)
{
BAD_FORMAT_NOTHROW_ASSERT(NULL == ((DelegateEEClass*)GetHalfBakedClass())->m_pInvokeMethod);
((DelegateEEClass*)GetHalfBakedClass())->m_pInvokeMethod = pNewMD;
}
else if (strcmp(pMethodName, "BeginInvoke") == 0)
{
BAD_FORMAT_NOTHROW_ASSERT(NULL == ((DelegateEEClass*)GetHalfBakedClass())->m_pBeginInvokeMethod);
((DelegateEEClass*)GetHalfBakedClass())->m_pBeginInvokeMethod = pNewMD;
}
else if (strcmp(pMethodName, "EndInvoke") == 0)
{
BAD_FORMAT_NOTHROW_ASSERT(NULL == ((DelegateEEClass*)GetHalfBakedClass())->m_pEndInvokeMethod);
((DelegateEEClass*)GetHalfBakedClass())->m_pEndInvokeMethod = pNewMD;
}
else
{
BuildMethodTableThrowException(IDS_CLASSLOAD_GENERAL);
}
// StoredSig specific intialization
{
StoredSigMethodDesc *pNewSMD = (StoredSigMethodDesc*) pNewMD;;
DWORD cSig;
PCCOR_SIGNATURE pSig = pIMDII->GetSigOfMethodDef(tok, &cSig);
pNewSMD->SetStoredMethodSig(pSig, cSig);
}
break;
case mcIL:
break;
case mcInstantiated:
{
// Initialize the typical instantiation.
InstantiatedMethodDesc* pNewIMD = (InstantiatedMethodDesc*) pNewMD;
pNewIMD->SetupGenericMethodDefinition(pIMDII,bmtDomain,GetModule(),
tok,IsTypicalTypeDefinition());
}
break;
default:
BAD_FORMAT_NOTHROW_ASSERT(!"Failed to set a method desc classification");
}
// Check the method desc's classification.
_ASSERTE(pNewMD->GetClassification() == (Classification & mdcClassification));
_ASSERTE(!pNewMD->IsMethodImpl() == !(Classification & mdcMethodImpl));
pNewMD->SetMemberDef(tok);
if (IsMdStatic(dwMemberAttrs))
pNewMD->SetStatic();
// Set suppress unmanaged code access permission attribute
pNewMD->ComputeSuppressUnmanagedCodeAccessAttr(pIMDII);
#ifdef _DEBUG
// Mark as many methods as synchronized as possible.
//
// Note that this can easily cause programs to deadlock, and that
// should not be treated as a bug in the program.
static ConfigDWORD stressSynchronized;
DWORD stressSynchronizedVal = stressSynchronized.val(L"stressSynchronized", 0);
bool isStressSynchronized = stressSynchronizedVal &&
pNewMD->IsIL() &&
((g_pValueTypeClass != NULL && g_pEnumClass != NULL &&
!IsValueClass()) || // Can not synchronize on byref "this"
IsMdStatic(dwMemberAttrs)) && // IsStatic() blows up in _DEBUG as pNewMD is not fully inited
g_pObjectClass != NULL; // Ignore Object:* since "this" could be a boxed object
// stressSynchronized=1 turns off the stress in the system domain to reduce
// the chances of spurious deadlocks. Deadlocks in user code can still occur.
// stressSynchronized=2 will probably cause more deadlocks, and is not recommended
if (stressSynchronizedVal == 1 && GetAssembly()->IsSystem())
isStressSynchronized = false;
if (IsMiSynchronized(dwImplFlags) || isStressSynchronized)
#else // !_DEBUG
if (IsMiSynchronized(dwImplFlags))
#endif // !_DEBUG
pNewMD->SetSynchronized();
#ifdef _DEBUG
pNewMD->m_pszDebugMethodName = (LPUTF8)pszDebugMethodName;
pNewMD->m_pszDebugClassName = (LPUTF8)pszDebugClassName;
pNewMD->m_pDebugMethodTable = GetHalfBakedMethodTable();
if (pszDebugMethodSignature == NULL)
pNewMD->m_pszDebugMethodSignature = FormatSig(pNewMD, bmtDomain, pamTracker);
else
pNewMD->m_pszDebugMethodSignature = pszDebugMethodSignature;
pNewMD->InitPrestubCallChecking();
#endif // _DEBUG
}
//*******************************************************************************
//
// Used by BuildMethodTable
//
VOID MethodTableBuilder::AddMethodImplDispatchMapping(
DispatchMapTypeID typeID,
UINT32 slotNumber,
MethodDesc* pMDImpl,
BOOL fIsVirtual)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
} CONTRACTL_END;
// Look for an existing entry in the map.
DispatchMapBuilder::Iterator it(bmtVT->pDispatchMapBuilder);
if (bmtVT->pDispatchMapBuilder->Find(typeID, slotNumber, it))
{
// Throw if this entry has already previously been MethodImpl'd.
if (it.IsMethodImpl())
{
// NOTE: This is where we check for duplicate overrides. This is the easiest place to check
// because duplicate overrides could in fact have separate MemberRefs to the same
// member and so just comparing tokens at the very start would not be enough.
if (it.GetTargetMD() != pMDImpl)
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MULTIPLEOVERRIDES, pMDImpl->GetMemberDef());
}
}
// This is the first MethodImpl. That's ok.
else
{
it.SetTarget(pMDImpl);
it.SetIsMethodImpl(TRUE);
}
}
// A mapping for this interface method does not exist, so insert it.
else
{
bmtVT->pDispatchMapBuilder->InsertMDMapping(
typeID, slotNumber, pMDImpl, fIsVirtual, TRUE);
}
// Save the entry into the vtable as well, if it isn't an interface methodImpl
if (typeID == DispatchMapTypeID::ThisClassID())
{
bmtVT->SetMethodDescForSlot(slotNumber, pMDImpl);
}
}
//*******************************************************************************
VOID MethodTableBuilder::MethodImplCompareSignatures(
mdMethodDef mdDecl,
IMDInternalImport* pImportDecl,
Module* pModuleDecl,
const Substitution* pSubstDecl,
mdMethodDef mdImpl,
IMDInternalImport* pImportImpl,
Module* pModuleImpl,
const Substitution* pSubstImpl,
PCCOR_SIGNATURE* ppImplSignature,
DWORD* pcImplSignature,
DWORD dwConstraintErrorCode)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
PRECONDITION(TypeFromToken(mdDecl) == mdtMethodDef);
PRECONDITION(TypeFromToken(mdImpl) == mdtMethodDef);
} CONTRACTL_END;
// Get the signature for the IMPL, if unavailable
if(*ppImplSignature == NULL)
{
*ppImplSignature = pImportImpl->GetSigOfMethodDef(mdImpl, pcImplSignature);
}
// Get the signature for the DECL
PCCOR_SIGNATURE pDeclSignature = NULL;
DWORD cDeclSignature = 0;
pDeclSignature = pImportDecl->GetSigOfMethodDef(mdDecl, &cDeclSignature);
// Compare the signatures
HRESULT hr = MetaSig::CompareMethodSigsNT(
pDeclSignature,
cDeclSignature,
pModuleDecl,
pSubstDecl,
*ppImplSignature,
*pcImplSignature,
pModuleImpl,
pSubstImpl);
// S_FALSE means the comparison was successful, but the signatures do not match
if (hr == S_FALSE)
{
hr = COR_E_TYPELOAD;
}
// Throw if the signatures do not match
if(FAILED(hr))
{
LOG((LF_CLASSLOADER, LL_INFO1000, "BADSIG placing MethodImpl: %x\n", mdDecl));
BuildMethodTableThrowException(hr, IDS_CLASSLOAD_MI_BADSIGNATURE, mdDecl);
}
//now compare the method constraints
if (!MetaSig::CompareMethodConstraints(pModuleImpl,mdImpl, pSubstDecl,pModuleDecl,mdDecl))
{
BuildMethodTableThrowException(dwConstraintErrorCode, mdImpl);
}
}
//*******************************************************************************
// 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::PlaceMethodImpls(AllocMemTracker *pamTracker)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END
HRESULT hr = S_OK;
if(bmtMethodImpl->pIndex == 0)
return;
DWORD pIndex = 0;
MethodDesc* next = bmtMethodImpl->GetBodyMethodDesc(pIndex);
// 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*) GetThread()->m_MarshalAlloc.Alloc((bmtVT->dwCurrentVtableSlot) * sizeof(DWORD));
MethodDesc **replaced = (MethodDesc**) GetThread()->m_MarshalAlloc.Alloc((bmtVT->dwCurrentVtableSlot) * sizeof(MethodDesc*));
while(next != NULL)
{
DWORD slotIndex = 0;
MethodDesc* body;
// 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;
// Get the MethodImpl storage
CONSISTENCY_CHECK(next->IsMethodImpl());
MethodImpl* pImpl = next->GetMethodImpl();
// 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);
if (bmtMethodImpl->IsBody(mdef))
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MULTIPLEOVERRIDES,
mdef);
}
// Throws
hr = PlaceLocalDeclaration(mdef,
body,
slots, // Adds override to the slot and replaced arrays.
replaced,
&slotIndex, // Increments count
&pBodySignature, // Fills in the signature
&cBodySignature);
}
else
{
// Do not use pDecl->IsInterface here as that asks the method table and the MT may not yet be set up.
if(pDecl->IsInterface())
{
// Throws
hr = PlaceInterfaceDeclaration(pDecl,
body,
bmtMethodImpl->GetDeclarationSubst(pIndex),
slots,
replaced,
&slotIndex, // Increments count
&pBodySignature, // Fills in the signature
&cBodySignature);
}
else
{
// Throws
hr = PlaceParentDeclaration(pDecl,
body,
bmtMethodImpl->GetDeclarationSubst(pIndex),
slots,
replaced,
&slotIndex, // Increments count
&pBodySignature, // Fills in the signature
&cBodySignature);
}
}
pIndex++;
// we hit the end of the list so leave
if(pIndex == bmtMethodImpl->pIndex)
next = NULL;
else
next = bmtMethodImpl->GetBodyMethodDesc(pIndex);
} while(next == body) ;
// Use the number of overrides to
// push information on to the method desc. We store the slots that
// are overridden and the method desc that is replaced. That way
// when derived classes need to determine if the method is to be
// overridden then it can check the name against the replaced
// method desc not the bodies name.
if(slotIndex == 0)
{
/*
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_DECLARATIONNOTFOUND, body->GetMemberDef());
*/
body->ResetMethodImpl();
}
else
{
hr = S_OK;
pImpl->SetSize(bmtDomain->GetHighFrequencyHeap(), pamTracker, slotIndex);
// Gasp we do a bubble sort. Should change this to a qsort..
for (DWORD i = 0; i < slotIndex; i++) {
for (DWORD j = i+1; j < slotIndex; j++)
{
if (slots[j] < slots[i])
{
MethodDesc* mTmp = replaced[i];
replaced[i] = replaced[j];
replaced[j] = mTmp;
DWORD sTmp = slots[i];
slots[i] = slots[j];
slots[j] = sTmp;
}
}
}
// Go and set the method impl
pImpl->SetData(slots, replaced);
}
} // while(next != NULL)
}
//*******************************************************************************
HRESULT MethodTableBuilder::PlaceLocalDeclaration(mdMethodDef mdef,
MethodDesc* pMDBody,
DWORD* slots,
MethodDesc** replaced,
DWORD* pSlotIndex,
PCCOR_SIGNATURE* ppBodySignature,
DWORD* pcBodySignature)
{
CONTRACTL
{
THROWS;
GC_TRIGGERS;
PRECONDITION(CheckPointer(bmtVT->pDispatchMapBuilder));
INJECT_FAULT(COMPlusThrowOM(););
}
CONTRACTL_END
////////////////////////////////////////////////////////////////////////////////
// First, find the method matching the token. Need to search for the MethodDesc
// that corresponds to this token since we need to know what slot the token has
// been assigned to for updating the vtable.
MethodDesc *pMDDecl = NULL;
{
DeclaredMethodIterator methIt(*this);
while (methIt.Next())
{
MethodDesc *pMD = methIt.GetMethodDesc();
PREFIX_ASSUME(pMD != NULL);
if ((pMD->GetMemberDef() == mdef))
{
pMDDecl = pMD;
break;
}
}
}
PREFIX_ASSUME(pMDDecl != NULL);
///////////////////////////////
// Verify the signatures match
MethodImplCompareSignatures(
pMDDecl->GetMemberDef(),
bmtInternal->pInternalImport,
bmtInternal->pModule,
NULL,
pMDBody->GetMemberDef(),
bmtInternal->pInternalImport,
bmtInternal->pModule,
NULL,
ppBodySignature,
pcBodySignature,
IDS_CLASSLOAD_CONSTRAINT_MISMATCH_ON_LOCAL_METHOD_IMPL);
///////////////////////////////
// Validate the method impl.
TestMethodImpl(
bmtInternal->pModule,
bmtInternal->pModule,
pMDDecl->GetMemberDef(),
pMDDecl->GetMemberDef());
// Don't allow overrides for any of the four special runtime implemented delegate methods
if (IsAnyDelegateClass())
{
IMDInternalImport *pMDInternalImport = bmtInternal->pInternalImport;
_ASSERTE(mdef == pMDDecl->GetMemberDef());
LPSTR strMethodName = (LPSTR)pMDInternalImport->GetNameOfMethodDef( mdef );
if ((strcmp(strMethodName, COR_CTOR_METHOD_NAME) == 0) ||
(strcmp(strMethodName, "Invoke") == 0) ||
(strcmp(strMethodName, "BeginInvoke") == 0) ||
(strcmp(strMethodName, "EndInvoke") == 0) )
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_CANNOT_OVERRIDE, mdef);
}
}
///////////////////
// Add the mapping
// Call helper to add it. Will throw if decl is already MethodImpl'd
AddMethodImplDispatchMapping(DispatchMapTypeID::ThisClassID(),
pMDDecl->GetSlot(),
pMDBody,
TRUE);
// We implement this slot, record it
slots[*pSlotIndex] = pMDDecl->GetSlot();
replaced[*pSlotIndex] = pMDDecl;
// increment the counter
(*pSlotIndex)++;
////////////
// Success!
return S_OK;
}
//*******************************************************************************
HRESULT MethodTableBuilder::PlaceInterfaceDeclaration(MethodDesc* pDeclMD,
MethodDesc* pImplMD,
const Substitution *pDeclSubst,
DWORD* slots,
MethodDesc** replaced,
DWORD* pSlotIndex,
PCCOR_SIGNATURE* ppBodySignature,
DWORD* pcBodySignature)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
INJECT_FAULT(return E_OUTOFMEMORY;);
PRECONDITION(CheckPointer(pDeclMD));
PRECONDITION(pDeclMD->IsInterface());
PRECONDITION(CheckPointer(bmtVT->pDispatchMapBuilder));
} CONTRACTL_END;
MethodTable *pDeclMT = pDeclMD->GetMethodTable();
//////////////////////////////////////////////////////////////
// First make sure the interface is implemented by this class
{
// Iterate all the interfaces in the map to find a match
BOOL fInterfaceFound = FALSE;
for (UINT32 i = 0; i < bmtInterface->dwInterfaceMapSize && !fInterfaceFound; i++)
{
MethodTable *pInterface = bmtInterface->pInterfaceMap[i].m_pMethodTable;
if (MetaSig::CompareTypeDefsUnderSubstitutions(pInterface,
pDeclMT,
bmtInterface->ppInterfaceSubstitutionChains[i],
pDeclSubst))
{
fInterfaceFound = TRUE;
break;
}
}
// Throw if this class does not implement the interface of pDecl.
if (!fInterfaceFound)
{
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_NOTIMPLEMENTED, pDeclMD->GetName());
}
}
///////////////////////////////
// Verify the signatures match
MethodImplCompareSignatures(
pDeclMD->GetMemberDef(),
pDeclMD->GetModule()->GetMDImport(),
pDeclMD->GetModule(),
pDeclSubst,
pImplMD->GetMemberDef(),
bmtInternal->pModule->GetMDImport(),
bmtInternal->pModule,
NULL,
ppBodySignature,
pcBodySignature,
IDS_CLASSLOAD_CONSTRAINT_MISMATCH_ON_INTERFACE_METHOD_IMPL);
///////////////////////////////
// Validate the method impl.
TestMethodImpl(
pDeclMD->GetModule(),
bmtInternal->pModule,
pDeclMD->GetMemberDef(),
pImplMD->GetMemberDef());
///////////////////
// Add the mapping
DispatchMapTypeID dispatchMapTypeID =
ComputeDispatchMapTypeID(pDeclMT, pDeclSubst);
CONSISTENCY_CHECK(dispatchMapTypeID.IsImplementedInterface());
// Call helper to add it. Will throw if decl is already MethodImpl'd
AddMethodImplDispatchMapping(dispatchMapTypeID,
pDeclMD->GetSlot(),
pImplMD,
TRUE);
////////////
// Success!
return S_OK;
}
//*******************************************************************************
HRESULT MethodTableBuilder::PlaceParentDeclaration(
MethodDesc* pMDDecl,
MethodDesc* pMDImpl,
const Substitution *pDeclSubst,
DWORD* slots,
MethodDesc** replaced,
DWORD* pSlotIndex,
PCCOR_SIGNATURE* ppImplSignature,
DWORD* pcImplSignature)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
PRECONDITION(CheckPointer(bmtVT->pDispatchMapBuilder));
INJECT_FAULT(COMPlusThrowOM(););
} CONTRACTL_END;
MethodTable *pMTDecl = pMDDecl->GetMethodTable();
// We're given a pMDDecl, but that could be for some parent way up which was overridden later on,
// so we translate to the most current decl for this slot so we make sure to take into consideration
// all the current attributes of the slot.
MethodDesc *pMDParentImpl = bmtParent->pParentMethodTable->GetUnknownMethodDescForSlot(pMDDecl->GetSlot());
pMDDecl = pMDParentImpl->GetDeclMethodDesc(pMDDecl->GetSlot());
////////////////////////////////////////////////////////////////
// Verify that the class of the declaration is in our heirarchy
{
MethodTable* pParent = bmtParent->pParentMethodTable;
Substitution* pParentSubst = &bmtParent->parentSubst;
Substitution* newSubst = NULL;
while(pParent != NULL) {
_ASSERT(pParent != NULL);
if (MetaSig::CompareTypeDefsUnderSubstitutions(pParent,
pMTDecl,
pParentSubst,
pDeclSubst))
{
break;
}
// Move on to the next parent...
newSubst = new Substitution;
*newSubst = pParent->GetClass()->GetSubstitutionForParent(pParentSubst);
pParentSubst = newSubst;
pParent = pParent->GetParentMethodTable();
}
if(newSubst != NULL) // there was at least 1 allocation
{
for(newSubst = pParentSubst;newSubst->GetNext()!=&bmtParent->parentSubst;
newSubst=(Substitution*)(newSubst->GetNext()));
memset(newSubst,0,sizeof(Substitution)); // destroy link to bmtParent->parentSubst
pParentSubst->DeleteChain(); // delete all chain up to and including newSubst
}
if(pParent == NULL) {
BuildMethodTableThrowException(IDS_CLASSLOAD_MI_NOTIMPLEMENTED,
pMDDecl->GetName());
}
}
/////////////////////////////////////////
// Verify that the signatures match
MethodImplCompareSignatures(
pMDDecl->GetMemberDef(),
pMDDecl->GetModule()->GetMDImport(),
pMDDecl->GetModule(),
pDeclSubst,
pMDImpl->GetMemberDef(),
bmtInternal->pInternalImport,
bmtInternal->pModule,
NULL,
ppImplSignature,
pcImplSignature,
IDS_CLASSLOAD_CONSTRAINT_MISMATCH_ON_PARENT_METHOD_IMPL);
////////////////////////////////
// Verify rules of method impls
TestMethodImpl(
pMDDecl->GetModule(),
bmtInternal->pModule,
pMDDecl->GetMemberDef(),
pMDImpl->GetMemberDef());
///////////////////
// Add the mapping
// Call helper to add it. Will throw if DECL is already MethodImpl'd
AddMethodImplDispatchMapping(DispatchMapTypeID::ThisClassID(),
pMDDecl->GetSlot(),
pMDImpl,
TRUE);
// We implement this slot, record it
slots[*pSlotIndex] = pMDDecl->GetSlot();
replaced[*pSlotIndex] = pMDDecl;
// increment the counter
(*pSlotIndex)++;
////////////
// Success!
return S_OK;
}
//*******************************************************************************
// This will validate that all interface methods that were matched during
// layout also validate against type constraints.
VOID MethodTableBuilder::ValidateInterfaceMethodConstraints()
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
} CONTRACTL_END;
DispatchMapBuilder::Iterator it(bmtVT->pDispatchMapBuilder);
while (it.IsValid())
{
if (it.GetTypeID() != DispatchMapTypeID::ThisClassID())
{
InterfaceInfo_t *pItfInfo = &bmtInterface->pInterfaceMap[it.GetTypeID().GetInterfaceNum()];
// Grab the substitution for this interface
Substitution *pSubst = bmtInterface->ppInterfaceSubstitutionChains[it.GetTypeID().GetInterfaceNum()];
// Grab the method token
MethodTable *pMTItf = pItfInfo->m_pMethodTable;
CONSISTENCY_CHECK(CheckPointer(pMTItf->GetMethodDescForSlot(it.GetSlotNumber())));
mdMethodDef mdTok = pItfInfo->m_pMethodTable->GetMethodDescForSlot(it.GetSlotNumber())->GetMemberDef();
// Default to the current module. The code immediately below determines if this
// assumption is incorrect.
Module * pTargetModule = bmtInternal->pModule;
// Get the module of the target method. Get it through the chunk to
// avoid triggering the assert that MethodTable is non-NULL. It may
// be null since it may belong to the type we're building right now.
MethodDesc * pTargetMD = it.GetTargetMD();
MethodDescChunk * pTargetChunk = pTargetMD->GetMethodDescChunk();
MethodTable * pTargetMT = pTargetChunk->GetMethodTable();
// If pTargetMT is null, this indicates that the target MethodDesc belongs
// to the current type. Otherwise, the MethodDesc MUST be owned by a parent
// of the type we're building.
BOOL fTargetIsOwnedByParent = pTargetMT != NULL;
// If the method is owned by a parent, we need to use the parent's module
if (fTargetIsOwnedByParent)
{
pTargetModule = pTargetMT->GetModule();
}
// Now compare the method constraints.
if (!MetaSig::CompareMethodConstraints(pTargetModule,
pTargetMD->GetMemberDef(),
pSubst,
pMTItf->GetModule(),
mdTok))
{
LOG((LF_CLASSLOADER, LL_INFO1000,
"BADCONSTRAINTS on interface method implementation: %x\n", pTargetMD));
// This exception will be due to an implicit implementation, since explicit errors
// will be detected in MethodImplCompareSignatures (for now, anyway).
CONSISTENCY_CHECK(!it.IsMethodImpl());
DWORD idsError = it.IsMethodImpl() ?
IDS_CLASSLOAD_CONSTRAINT_MISMATCH_ON_INTERFACE_METHOD_IMPL :
IDS_CLASSLOAD_CONSTRAINT_MISMATCH_ON_IMPLICIT_IMPLEMENTATION;
if (fTargetIsOwnedByParent)
{
DefineFullyQualifiedNameForClass();
LPCUTF8 szClassName = GetFullyQualifiedNameForClassNestedAware(pTargetMD->GetClass());
LPCUTF8 szMethodName = pTargetMD->GetName();
CQuickBytes qb;
// allocate enough room for "<class>.<method>\0"
size_t cchFullName = strlen(szClassName) + 1 + strlen(szMethodName) + 1;
LPUTF8 szFullName = (LPUTF8) qb.AllocThrows(cchFullName);
strcpy_s(szFullName, cchFullName, szClassName);
strcat_s(szFullName, cchFullName, ".");
strcat_s(szFullName, cchFullName, szMethodName);
BuildMethodTableThrowException(idsError, szFullName);
}
else
{
BuildMethodTableThrowException(idsError, pTargetMD->GetMemberDef());
}
}
}
// Move to the next entry
it.Next();
}
}
//
// Used by BuildMethodTable
//
// If we're a value class, we want to create duplicate slots and MethodDescs for all methods in the vtable
// section (i.e. not non-virtual instance methods or statics).
// In the name of uniformity it would be much nicer
// if we created _all_ value class BoxedEntryPointStubs at this point.
// However, non-virtual instance methods only require unboxing
// stubs in the rare case that we create a delegate to such a
// method, and thus it would be inefficient to create them on
// loading: after all typical structs will have many non-virtual
// instance methods.
//
// Unboxing stubs for non-virtual instance methods are created
// in MethodDesc::FindOrCreateAssociatedMethodDesc.
VOID MethodTableBuilder::ChangeValueClassVirtualsToBoxedEntryPointsAndCreateUnboxedEntryPoints(AllocMemTracker *pamTracker)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
} CONTRACTL_END;
// If we're a value class, we want to create duplicate slots and MethodDescs for all methods in the vtable
// section (i.e. not privates or statics).
if (IsValueClass())
{
DeclaredMethodIterator it(*this);
while (it.Next())
{
MethodDesc *pMD;
pMD = it.GetMethodDesc();
if (pMD == NULL)
continue;
if (IsMdStatic(it.Attrs()) ||
!IsMdVirtual(it.Attrs()) ||
(it.Classification() & mdcClassification) == mcInstantiated ||
IsMdRTSpecialName(it.Attrs()))
continue;
bmtTokenRangeNode *pTR = GetTokenRange(it.Token(),
&(bmtMetaData->ranges[it.MethodType()][it.MethodImpl()]));
bmtMethodDescSet *set = &bmtMFDescs->sets[it.MethodType()][it.MethodImpl()];
MethodDescChunk * pChunk = set->pChunkList[pTR->dwCurrentChunk];
MethodDesc *pNewMD = pChunk->GetMethodDescAt(pTR->dwCurrentIndex);
// <NICE> memcpy operations on data structures like MethodDescs are extremely fragile
// and should not be used. We should go to the effort of having proper constructors
// in the MethodDesc class. </NICE>
memcpy(pNewMD, pMD, pChunk->GetMethodDescSize() - METHOD_PREPAD);
// Reset the chunk index
pNewMD->SetChunkIndex(pChunk, pTR->dwCurrentIndex);
pNewMD->SetMemberDef(pMD->GetMemberDef());
// Update counters to prepare for next method desc allocation.
pTR->dwCurrentIndex++;
if (pTR->dwCurrentIndex == MethodDescChunk::GetMaxMethodDescs(it.Classification()))
{
pTR->dwCurrentChunk++;
pTR->dwCurrentIndex = 0;
}
bmtMFDescs->ppUnboxMethodDescList[it.CurrentIndex()] = pNewMD;
pNewMD->SetSlot((WORD) bmtVT->dwCurrentNonVtableSlot);
// Change the original MD in the vtable section
// to be a stub method which takes a BOXed this pointer.
pMD->SetIsUnboxingStub();
bmtVT->pNonVtableMD[ bmtVT->dwCurrentNonVtableSlot ] = pNewMD; // not pre-stub addr, refer to statics above
bmtVT->dwCurrentNonVtableSlot++;
}
}
}
//*******************************************************************************
//
// Used by BuildMethodTable
//
//
// 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.
//
// BEHAVIOUR (based on Partition II: 11.2, not including MethodImpls)
// C is current class, P is a parent class, I is the interface being implemented
//
// FOREACH interface I implemented by this class C
// FOREACH method I::M
// IF I is EXPLICITLY implemented by C
// IF some method C::M matches I::M
// USE C::M as implementation for I::M
// ELIF we inherit a method P::M that matches I::M
// USE P::M as implementation for I::M
// ENDIF
// ELSE
// IF I::M lacks implementation
// IF some method C::M matches I::M
// USE C::M as implementation for I::M
// ELIF we inherit a method P::M that matches I::M
// USE P::M as implementation for I::M
// ENDIF
// ENDIF
// ENDIF
// ENDFOR
// ENDFOR
//
VOID MethodTableBuilder::PlaceVtableMethods(
DWORD numDeclaredInterfaces,
BuildingInterfaceInfo_t *pBuildingInterfaceList)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
INJECT_FAULT(COMPlusThrowOM(););
} CONTRACTL_END;
BOOL fParentInterface;
for (DWORD dwCurInterface = 0;
dwCurInterface < bmtInterface->dwInterfaceMapSize;
dwCurInterface++)
{
// Default to not being implemented by the current class
fParentInterface = FALSE;
// Keep track of the current interface
InterfaceInfo_t *pCurItfInfo = &(bmtInterface->pInterfaceMap[dwCurInterface]);
// The interface we are attempting to place
MethodTable* pInterface = pCurItfInfo->m_pMethodTable;
Substitution* pIntfSubst = bmtInterface->ppInterfaceSubstitutionChains[dwCurInterface];
// Check if this type is allowed to implement this interface.
{
Assembly *pItfAssembly = pInterface->GetAssembly();
Assembly *pThisAssembly = bmtInternal->pModule->GetAssembly();
if(pCurItfInfo->IsDeclaredOnClass() &&
!pInterface->IsExternallyVisible() &&
pItfAssembly !=