diff --git a/src/coreclr/dlls/mscorrc/mscorrc.rc b/src/coreclr/dlls/mscorrc/mscorrc.rc index a2a7b0ebefe1dd..20390aec94eb11 100644 --- a/src/coreclr/dlls/mscorrc/mscorrc.rc +++ b/src/coreclr/dlls/mscorrc/mscorrc.rc @@ -400,6 +400,7 @@ BEGIN IDS_CLASSLOAD_MI_MISSING_SIG_BODY "Signature for the body in a method implementation cannot be found. Type: '%1'. Assembly: '%2'." IDS_CLASSLOAD_MI_MISSING_SIG_DECL "Signature for the declaration in a method implementation cannot be found. Type: '%1'. Assembly: '%2'." IDS_CLASSLOAD_MI_BADRETURNTYPE "Return type in method '%1' on type '%2' from assembly '%3' is not compatible with base type method '%4'." + IDS_CLASSLOAD_STATICVIRTUAL_NOTIMPL "Virtual static method '%3' is not implemented on type '%1' from assembly '%2'." IDS_CLASSLOAD_EQUIVALENTSTRUCTMETHODS "Could not load the structure '%1' from assembly '%2'. The structure is marked as eligible for type equivalence, but it has a method." IDS_CLASSLOAD_EQUIVALENTSTRUCTFIELDS "Could not load the structure '%1' from assembly '%2'. The structure is marked as eligible for type equivalence, but it has a static or non-public field." diff --git a/src/coreclr/dlls/mscorrc/resource.h b/src/coreclr/dlls/mscorrc/resource.h index a44e365fbc5b50..b26dc8567426ca 100644 --- a/src/coreclr/dlls/mscorrc/resource.h +++ b/src/coreclr/dlls/mscorrc/resource.h @@ -168,6 +168,7 @@ #define IDS_CLASSLOAD_MI_MISSING_SIG_BODY 0x17a6 #define IDS_CLASSLOAD_MI_MISSING_SIG_DECL 0x17a7 #define IDS_CLASSLOAD_MI_BADRETURNTYPE 0x17a8 +#define IDS_CLASSLOAD_STATICVIRTUAL_NOTIMPL 0x17a9 #define IDS_CLASSLOAD_TOOMANYGENERICARGS 0x17ab #define IDS_ERROR 0x17b0 diff --git a/src/coreclr/vm/genmeth.cpp b/src/coreclr/vm/genmeth.cpp index 2d2a3965d9db10..5e9193b641fdc7 100644 --- a/src/coreclr/vm/genmeth.cpp +++ b/src/coreclr/vm/genmeth.cpp @@ -1180,7 +1180,10 @@ MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD, pExactMT->GetCanonicalMethodTable(), FALSE, Instantiation(repInst, methodInst.GetNumArgs()), - TRUE); + /* allowInstParam */ TRUE, + /* forceRemotableMethod */ FALSE, + /* allowCreate */ TRUE, + /* level */ level); _ASSERTE(pWrappedMD->IsSharedByGenericInstantiations()); _ASSERTE(!methodInst.IsEmpty() || !pWrappedMD->IsSharedByGenericMethodInstantiations()); diff --git a/src/coreclr/vm/memberload.cpp b/src/coreclr/vm/memberload.cpp index 313e616568d2d0..7c8f9b6c166e2e 100644 --- a/src/coreclr/vm/memberload.cpp +++ b/src/coreclr/vm/memberload.cpp @@ -562,7 +562,8 @@ FieldDesc * MemberLoader::GetFieldDescFromMemberRefAndType(Module * pModule, // MethodDesc* MemberLoader::GetMethodDescFromMethodDef(Module *pModule, mdToken MethodDef, - BOOL strictMetadataChecks) + BOOL strictMetadataChecks, + ClassLoadLevel owningTypeLoadLevel) { CONTRACTL { @@ -623,7 +624,7 @@ MethodDesc* MemberLoader::GetMethodDescFromMethodDef(Module *pModule, } } - pMD->CheckRestore(); + pMD->CheckRestore(owningTypeLoadLevel); #if 0 // Generics: enable this check after the findMethod call in the Zapper which passes @@ -713,7 +714,8 @@ MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec( BOOL strictMetadataChecks, // Normally true - the zapper is one exception. Throw an exception if no generic method args // given for a generic method, otherwise return the 'generic' instantiation - BOOL allowInstParam) + BOOL allowInstParam, + ClassLoadLevel owningTypeLoadLevel) { CONTRACTL { @@ -738,7 +740,7 @@ MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec( switch (TypeFromToken(MemberRef)) { case mdtMethodDef: - pMD = GetMethodDescFromMethodDef(pModule, MemberRef, strictMetadataChecks); + pMD = GetMethodDescFromMethodDef(pModule, MemberRef, strictMetadataChecks, owningTypeLoadLevel); th = pMD->GetMethodTable(); break; @@ -770,7 +772,10 @@ MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec( th.GetMethodTable(), FALSE /* don't get unboxing entry point */, strictMetadataChecks ? Instantiation() : pMD->LoadMethodInstantiation(), - allowInstParam); + allowInstParam, + /* forceRemotableMethod */ FALSE, + /* allowCreate */ TRUE, + /* level */ owningTypeLoadLevel); } // MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec //--------------------------------------------------------------------------------------- diff --git a/src/coreclr/vm/memberload.h b/src/coreclr/vm/memberload.h index da77684fb227ea..1b15a91421ee30 100644 --- a/src/coreclr/vm/memberload.h +++ b/src/coreclr/vm/memberload.h @@ -74,7 +74,8 @@ class MemberLoader mdToken MemberRefOrDefOrSpec, const SigTypeContext *pTypeContext, // Context for type parameters in any parent TypeSpec and in the instantiation in a MethodSpec BOOL strictMetadataChecks, // Normally true - the zapper is one exception. Throw an exception if no generic method args given for a generic method, otherwise return the 'generic' instantiation - BOOL allowInstParam); + BOOL allowInstParam, + ClassLoadLevel owningTypeLoadLevel = CLASS_LOADED); static FieldDesc* GetFieldDescFromMemberDefOrRef(Module *pModule, mdMemberRef MemberDefOrRef, @@ -92,7 +93,8 @@ class MemberLoader static MethodDesc* GetMethodDescFromMethodDef(Module *pModule, mdToken MethodDef, - BOOL strictMetadataChecks); + BOOL strictMetadataChecks, + ClassLoadLevel owningTypeLoadLevel = CLASS_LOADED); static FieldDesc* GetFieldDescFromFieldDef(Module *pModule, mdToken FieldDef, diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index a4020a875afb84..671ec647907ec5 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -5659,6 +5659,22 @@ void MethodTable::DoFullyLoad(Generics::RecursionGraph * const pVisited, const } else { + // Validate implementation of virtual static methods on all implemented interfaces unless: + // 1) The type resides in the system module (System.Private.CoreLib); we own this module and ensure + // its consistency by other means not requiring runtime checks; + // 2) There are no virtual static methods defined on any of the interfaces implemented by this type; + // 3) The method is abstract in which case it's allowed to leave some virtual static methods unimplemented + // akin to equivalent behavior of virtual instance method overriding in abstract classes; + // 4) The type is a shared generic in which case we generally don't have enough information to perform + // the validation. + if (!GetModule()->IsSystem() && + HasVirtualStaticMethods() && + !IsAbstract() && + !IsSharedByGenericInstantiations()) + { + VerifyThatAllVirtualStaticMethodsAreImplemented(); + } + // Finally, mark this method table as fully loaded SetIsFullyLoaded(); } @@ -9173,7 +9189,7 @@ MethodDesc *MethodTable::GetDefaultConstructor(BOOL forceBoxedEntryPoint /* = FA //========================================================================================== // Finds the (non-unboxing) MethodDesc that implements the interface virtual static method pInterfaceMD. MethodDesc * -MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult) +MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult, BOOL checkDuplicates) { if (!pInterfaceMD->IsSharedByGenericMethodInstantiations() && !pInterfaceType->IsSharedByGenericInstantiations()) { @@ -9203,7 +9219,7 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* // Search for match on a per-level in the type hierarchy for (MethodTable* pMT = this; pMT != nullptr; pMT = pMT->GetParentMethodTable()) { - MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceType, pInterfaceMD); + MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceType, pInterfaceMD, checkDuplicates); if (pMD != nullptr) { return pMD; @@ -9253,7 +9269,7 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* // Try to locate the appropriate MethodImpl matching a given interface static virtual method. // Returns nullptr on failure. MethodDesc* -MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD) +MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL checkDuplicates) { HRESULT hr = S_OK; IMDInternalImport* pMDInternalImport = GetMDImport(); @@ -9294,7 +9310,11 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType MethodTable *pInterfaceMT = ClassLoader::LoadTypeDefOrRefOrSpecThrowing( GetModule(), tkParent, - &sigTypeContext) + &sigTypeContext, + ClassLoader::ThrowIfNotFound, + ClassLoader::FailIfUninstDefOrRef, + ClassLoader::LoadTypes, + CLASS_LOAD_EXACTPARENTS) .GetMethodTable(); if (pInterfaceMT != pInterfaceType) { @@ -9305,7 +9325,8 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType methodDecl, &sigTypeContext, /* strictMetadataChecks */ FALSE, - /* allowInstParam */ FALSE); + /* allowInstParam */ FALSE, + /* owningTypeLoadLevel */ CLASS_LOAD_EXACTPARENTS); if (pMethodDecl == nullptr) { COMPlusThrow(kTypeLoadException, E_FAIL); @@ -9326,7 +9347,8 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType methodBody, &sigTypeContext, /* strictMetadataChecks */ FALSE, - /* allowInstParam */ FALSE); + /* allowInstParam */ FALSE, + /* owningTypeLoadLevel */ CLASS_LOAD_EXACTPARENTS); if (pMethodImpl == nullptr) { COMPlusThrow(kTypeLoadException, E_FAIL); @@ -9341,15 +9363,56 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType if (pInterfaceMD->HasMethodInstantiation() || pMethodImpl->HasMethodInstantiation() || HasInstantiation()) { - return pMethodImpl->FindOrCreateAssociatedMethodDesc(pMethodImpl, this, FALSE, pInterfaceMD->GetMethodInstantiation(), /* allowInstParam */ FALSE); + pMethodImpl = pMethodImpl->FindOrCreateAssociatedMethodDesc( + pMethodImpl, + this, + FALSE, + pInterfaceMD->GetMethodInstantiation(), + /* allowInstParam */ FALSE, + /* forceRemotableMethod */ FALSE, + /* allowCreate */ TRUE, + /* level */ CLASS_LOAD_EXACTPARENTS); } - else + if (pMethodImpl != nullptr) { - return pMethodImpl; + if (!checkDuplicates) + { + return pMethodImpl; + } + if (pPrevMethodImpl != nullptr) + { + // Two MethodImpl records found for the same virtual static interface method + COMPlusThrow(kTypeLoadException, E_FAIL); + } + pPrevMethodImpl = pMethodImpl; } } - return nullptr; + return pPrevMethodImpl; +} + +void +MethodTable::VerifyThatAllVirtualStaticMethodsAreImplemented() +{ + InterfaceMapIterator it = IterateInterfaceMap(); + while (it.Next()) + { + MethodTable *pInterfaceMT = it.GetInterface(); + if (pInterfaceMT->HasVirtualStaticMethods()) + { + for (MethodIterator it(pInterfaceMT); it.IsValid(); it.Next()) + { + MethodDesc *pMD = it.GetMethodDesc(); + if (pMD->IsVirtual() && + pMD->IsStatic() && + !ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* checkDuplicates */ TRUE)) + { + IMDInternalImport* pInternalImport = GetModule()->GetMDImport(); + GetModule()->GetAssembly()->ThrowTypeLoadException(pInternalImport, GetCl(), pMD->GetName(), IDS_CLASSLOAD_STATICVIRTUAL_NOTIMPL); + } + } + } + } } //========================================================================================== diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 4a6bdda778ee93..b3d2aa48601079 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -1560,6 +1560,8 @@ class MethodTable inline BOOL HasVirtualStaticMethods() const; inline void SetHasVirtualStaticMethods(); + void VerifyThatAllVirtualStaticMethodsAreImplemented(); + inline WORD GetNumVirtuals() { LIMITED_METHOD_DAC_CONTRACT; @@ -2280,7 +2282,7 @@ class MethodTable // Resolve virtual static interface method pInterfaceMD on this type. - MethodDesc *ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult); + MethodDesc *ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult, BOOL checkDuplicates = FALSE); // Try a partial resolve of the constraint call, up to generic code sharing. // @@ -2397,7 +2399,7 @@ class MethodTable // Try to resolve a given static virtual method override on this type. Return nullptr // when not found. - MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD); + MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL checkDuplicates); public: static MethodDesc *MapMethodDeclToMethodImpl(MethodDesc *pMDDecl); diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index ffa1d35155a6b3..daaf1bb32cb768 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -320,6 +320,11 @@ MethodTableBuilder::ExpandApproxInterface( { STANDARD_VM_CONTRACT; + if (pNewInterface->HasVirtualStaticMethods()) + { + bmtProp->fHasVirtualStaticMethods = TRUE; + } + //#ExpandingInterfaces // We expand the tree of inherited interfaces into a set by adding the // current node BEFORE expanding the parents of the current node.