From 333c4e72b83a08288db929ac56c0e336c936b655 Mon Sep 17 00:00:00 2001 From: David Wrighton Date: Fri, 25 Jun 2021 15:06:22 -0700 Subject: [PATCH] Fix handling of static virtual method implementation checking (#54710) - It turns out that GetMethodDescFromMemberDefOrRefOrSpec and FindOrCreateAssociatedMethodDesc are not safe to use on a MemberRef whent the associated MethodTable is not fully loaded. - Instead only use that feature when working with a MethodDef or a fully loaded type, and when working with a not fully loaded type, use MemberLoader::FindMethod instead. - When running the resolution algorithm for doing constraint validation, it also is not necessary to fully resolve to the exact correct MethodDesc, which as that process uses FindOrCreateAssociatedMethodDesc needs to be avoided. - The above was not evident as in many cases, the validation algorithm did not run as it was misplaced and located directly before the call to SetIsFullyLoaded. That code path is only followed if the type is able to fully load without circular dependencies. (Test case CuriouslyRecurringGenericWithUnimplementedMethod added to cover that scenario) - In addition, while investigating these issues, I realized we were lacking checks that the constraints on the impl and decl method were not checked at during type load, but that work was instead deferred to dispatch time. Along with the constraint check there was also a set of accessibility checks that had been missed that are common to all MethodImpl handling. Fix by adding tweaking the logic to share most of that code. --- src/coreclr/vm/methodtable.cpp | 84 ++++++++++++++-------- src/coreclr/vm/methodtable.h | 8 ++- src/coreclr/vm/methodtablebuilder.cpp | 80 +++++++++++++++++---- src/coreclr/vm/methodtablebuilder.h | 16 ++++- src/coreclr/vm/runtimehandles.cpp | 2 +- src/coreclr/vm/typedesc.cpp | 2 +- ...ouslyRecurringGenericWithUnimplementedMethod.il | 57 +++++++++++++++ ...yRecurringGenericWithUnimplementedMethod.ilproj | 12 ++++ .../NegativeTestCases/MethodConstraintMismatch.il | 66 +++++++++++++++++ .../MethodConstraintMismatch.ilproj | 12 ++++ 10 files changed, 290 insertions(+), 49 deletions(-) create mode 100644 src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/CuriouslyRecurringGenericWithUnimplementedMethod.il create mode 100644 src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/CuriouslyRecurringGenericWithUnimplementedMethod.ilproj create mode 100644 src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/MethodConstraintMismatch.il create mode 100644 src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/MethodConstraintMismatch.ilproj diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 58e6418..bb44e82 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -5658,6 +5658,22 @@ void MethodTable::DoFullyLoad(Generics::RecursionGraph * const pVisited, const } + // Validate implementation of virtual static methods on all implemented interfaces unless: + // 1) The type resides in a module where sanity checks are disabled (such as System.Private.CoreLib, or an + // R2R module with type checks disabled) + // 2) There are no virtual static methods defined on any of the interfaces implemented by this type; + // 3) The type 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 not the typical type definition. (The typical type is always checked) + + if (fNeedsSanityChecks && + IsTypicalTypeDefinition() && + !IsAbstract()) + { + if (HasVirtualStaticMethods()) + VerifyThatAllVirtualStaticMethodsAreImplemented(); + } + if (locals.fBailed) { // We couldn't complete security checks on some dependency because it is already being processed by one of our callers. @@ -5671,22 +5687,6 @@ 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(); } @@ -9201,7 +9201,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, BOOL checkDuplicates, BOOL allowVariantMatches) +MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult, BOOL verifyImplemented, BOOL allowVariantMatches) { if (!pInterfaceMD->IsSharedByGenericMethodInstantiations() && !pInterfaceType->IsSharedByGenericInstantiations()) { @@ -9231,7 +9231,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, checkDuplicates); + MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceType, pInterfaceMD, verifyImplemented); if (pMD != nullptr) { return pMD; @@ -9273,7 +9273,7 @@ MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* { // Variant or equivalent matching interface found // Attempt to resolve on variance matched interface - pMD = pMT->TryResolveVirtualStaticMethodOnThisType(it.GetInterface(), pInterfaceMD, checkDuplicates); + pMD = pMT->TryResolveVirtualStaticMethodOnThisType(it.GetInterface(), pInterfaceMD, verifyImplemented); if (pMD != nullptr) { return pMD; @@ -9295,7 +9295,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, BOOL checkDuplicates) +MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented) { HRESULT hr = S_OK; IMDInternalImport* pMDInternalImport = GetMDImport(); @@ -9347,13 +9347,39 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType { continue; } - MethodDesc *pMethodDecl = MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec( + MethodDesc *pMethodDecl; + + if ((TypeFromToken(methodDecl) == mdtMethodDef) || pInterfaceMT->IsFullyLoaded()) + { + pMethodDecl = MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec( GetModule(), methodDecl, &sigTypeContext, /* strictMetadataChecks */ FALSE, /* allowInstParam */ FALSE, /* owningTypeLoadLevel */ CLASS_LOAD_EXACTPARENTS); + } + else if (TypeFromToken(methodDecl) == mdtMemberRef) + { + LPCUTF8 szMember; + PCCOR_SIGNATURE pSig; + DWORD cSig; + + IfFailThrow(pMDInternalImport->GetNameAndSigOfMemberRef(methodDecl, &pSig, &cSig, &szMember)); + + // Do a quick name check to avoid excess use of FindMethod + if (strcmp(szMember, pInterfaceMD->GetName()) != 0) + { + continue; + } + + pMethodDecl = MemberLoader::FindMethod(pInterfaceMT, szMember, pSig, cSig, GetModule()); + } + else + { + COMPlusThrow(kTypeLoadException, E_FAIL); + } + if (pMethodDecl == nullptr) { COMPlusThrow(kTypeLoadException, E_FAIL); @@ -9369,13 +9395,11 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType COMPlusThrow(kTypeLoadException, E_FAIL); } - MethodDesc *pMethodImpl = MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec( + MethodDesc *pMethodImpl = MemberLoader::GetMethodDescFromMethodDef( GetModule(), methodBody, - &sigTypeContext, - /* strictMetadataChecks */ FALSE, - /* allowInstParam */ FALSE, - /* owningTypeLoadLevel */ CLASS_LOAD_EXACTPARENTS); + FALSE, + CLASS_LOAD_EXACTPARENTS); if (pMethodImpl == nullptr) { COMPlusThrow(kTypeLoadException, E_FAIL); @@ -9388,7 +9412,7 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType COMPlusThrow(kTypeLoadException, E_FAIL); } - if (pInterfaceMD->HasMethodInstantiation() || pMethodImpl->HasMethodInstantiation() || HasInstantiation()) + if (!verifyImplemented) { pMethodImpl = pMethodImpl->FindOrCreateAssociatedMethodDesc( pMethodImpl, @@ -9398,11 +9422,11 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType /* allowInstParam */ FALSE, /* forceRemotableMethod */ FALSE, /* allowCreate */ TRUE, - /* level */ CLASS_LOAD_EXACTPARENTS); + /* level */ CLASS_LOADED); } if (pMethodImpl != nullptr) { - if (!checkDuplicates) + if (!verifyImplemented) { return pMethodImpl; } @@ -9432,7 +9456,7 @@ MethodTable::VerifyThatAllVirtualStaticMethodsAreImplemented() MethodDesc *pMD = it.GetMethodDesc(); if (pMD->IsVirtual() && pMD->IsStatic() && - !ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* checkDuplicates */ TRUE, /* allowVariantMatches */ FALSE)) + !ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* verifyImplemented */ TRUE, /* allowVariantMatches */ FALSE)) { 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 326bcce..3d0ee06b 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -2285,7 +2285,11 @@ public: // Resolve virtual static interface method pInterfaceMD on this type. - MethodDesc *ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult, BOOL checkDuplicates = FALSE, BOOL allowVariantMatches = TRUE); + // + // Specify allowNullResult to return NULL instead of throwing if the there is no implementation + // Specify verifyImplemented to verify that there is a match, but do not actually return a final useable MethodDesc + // Specify allowVariantMatches to permit generic interface variance + MethodDesc *ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult, BOOL verifyImplemented = FALSE, BOOL allowVariantMatches = TRUE); // Try a partial resolve of the constraint call, up to generic code sharing. // @@ -2402,7 +2406,7 @@ public: // Try to resolve a given static virtual method override on this type. Return nullptr // when not found. - MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL checkDuplicates); + MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL verifyImplemented); public: static MethodDesc *MapMethodDeclToMethodImpl(MethodDesc *pMDDecl); diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index ad82732..9ccb3d7 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -1186,12 +1186,29 @@ MethodTableBuilder::bmtInterfaceEntry::CreateSlotTable( CONSISTENCY_CHECK(m_pImplTable == NULL); SLOT_INDEX cSlots = (SLOT_INDEX)GetInterfaceType()->GetMethodTable()->GetNumVirtuals(); - bmtInterfaceSlotImpl * pST = new (pStackingAllocator) bmtInterfaceSlotImpl[cSlots]; + SLOT_INDEX cSlotsTotal = cSlots; + + if (GetInterfaceType()->GetMethodTable()->HasVirtualStaticMethods()) + { + MethodTable::MethodIterator it(GetInterfaceType()->GetMethodTable()); + for (; it.IsValid(); it.Next()) + { + MethodDesc *pDeclMD = it.GetDeclMethodDesc(); + if (pDeclMD->IsStatic() && pDeclMD->IsVirtual()) + { + cSlotsTotal++; + } + } + } + + bmtInterfaceSlotImpl * pST = new (pStackingAllocator) bmtInterfaceSlotImpl[cSlotsTotal]; + MethodTable::MethodIterator it(GetInterfaceType()->GetMethodTable()); for (; it.IsValid(); it.Next()) { - if (!it.IsVirtual()) + MethodDesc *pDeclMD = it.GetDeclMethodDesc(); + if (!pDeclMD->IsVirtual()) { break; } @@ -1199,8 +1216,15 @@ MethodTableBuilder::bmtInterfaceEntry::CreateSlotTable( bmtRTMethod * pCurMethod = new (pStackingAllocator) bmtRTMethod(GetInterfaceType(), it.GetDeclMethodDesc()); - CONSISTENCY_CHECK(m_cImplTable == it.GetSlotNumber()); - pST[m_cImplTable++] = bmtInterfaceSlotImpl(pCurMethod, INVALID_SLOT_INDEX); + if (pDeclMD->IsStatic()) + { + pST[cSlots + m_cImplTableStatics++] = bmtInterfaceSlotImpl(pCurMethod, INVALID_SLOT_INDEX); + } + else + { + CONSISTENCY_CHECK(m_cImplTable == it.GetSlotNumber()); + pST[m_cImplTable++] = bmtInterfaceSlotImpl(pCurMethod, INVALID_SLOT_INDEX); + } } m_pImplTable = pST; @@ -4808,16 +4832,16 @@ VOID MethodTableBuilder::TestMethodImpl( { BuildMethodTableThrowException(IDS_CLASSLOAD_MI_NONVIRTUAL_DECL); } - if (!IsMdVirtual(dwImplAttrs)) + if ((IsMdVirtual(dwImplAttrs) && IsMdStatic(dwImplAttrs)) || (!IsMdVirtual(dwImplAttrs) && !IsMdStatic(dwImplAttrs))) { BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL); } - // Virtual methods cannot be static - if (IsMdStatic(dwDeclAttrs)) + // Virtual methods on classes/valuetypes cannot be static + if (IsMdStatic(dwDeclAttrs) && !hDeclMethod.GetOwningType().IsInterface()) { BuildMethodTableThrowException(IDS_CLASSLOAD_STATICVIRTUAL); } - if (IsMdStatic(dwImplAttrs)) + if ((!!IsMdStatic(dwImplAttrs)) != (!!IsMdStatic(dwDeclAttrs))) { BuildMethodTableThrowException(IDS_CLASSLOAD_STATICVIRTUAL); } @@ -5421,14 +5445,14 @@ MethodTableBuilder::PlaceVirtualMethods() // that the name+signature corresponds to. Used by ProcessMethodImpls and ProcessInexactMethodImpls // Always returns the first match that it finds. Affects the ambiguities in code:#ProcessInexactMethodImpls_Ambiguities MethodTableBuilder::bmtMethodHandle -MethodTableBuilder::FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry, MethodSignature &declSig) +MethodTableBuilder::FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry, MethodSignature &declSig, bool searchForStaticMethods) { STANDARD_VM_CONTRACT; bmtMethodHandle declMethod; bmtInterfaceEntry::InterfaceSlotIterator slotIt = - pItfEntry->IterateInterfaceSlots(GetStackingAllocator()); + pItfEntry->IterateInterfaceSlots(GetStackingAllocator(), searchForStaticMethods); // Check for exact match for (; !slotIt.AtEnd(); slotIt.Next()) { @@ -5656,7 +5680,7 @@ MethodTableBuilder::ProcessMethodImpls() DeclaredMethodIterator it(*this); while (it.Next()) { - if (!IsMdVirtual(it.Attrs()) && it.IsMethodImpl()) + if (!IsMdVirtual(it.Attrs()) && it.IsMethodImpl() && bmtProp->fNoSanityChecks) { // Non-virtual methods can only be classified as methodImpl when implementing // static virtual methods. @@ -5839,7 +5863,7 @@ MethodTableBuilder::ProcessMethodImpls() } // 3. Find the matching method. - declMethod = FindDeclMethodOnInterfaceEntry(pItfEntry, declSig); + declMethod = FindDeclMethodOnInterfaceEntry(pItfEntry, declSig, !IsMdVirtual(it.Attrs())); // Search for statics when the impl is non-virtual } else { @@ -5874,6 +5898,14 @@ MethodTableBuilder::ProcessMethodImpls() BuildMethodTableThrowException(IDS_CLASSLOAD_MI_MUSTBEVIRTUAL, it.Token()); } + if (!IsMdVirtual(it.Attrs()) && it.IsMethodImpl() && IsMdStatic(it.Attrs())) + { + // Non-virtual methods can only be classified as methodImpl when implementing + // static virtual methods. + ValidateStaticMethodImpl(declMethod, *it);//bmtMethodHandle(pCurImplMethod)); + continue; + } + if (bmtMetaData->rgMethodImplTokens[m].fRequiresCovariantReturnTypeChecking) { it->GetMethodDesc()->SetRequiresCovariantReturnTypeChecking(); @@ -6744,6 +6776,30 @@ MethodTableBuilder::PlaceParentDeclarationOnClass( (*pSlotIndex)++; } // MethodTableBuilder::PlaceParentDeclarationOnClass +VOID MethodTableBuilder::ValidateStaticMethodImpl( + bmtMethodHandle hDecl, + bmtMethodHandle hImpl) +{ + // While we don't want to place the static method impl declarations on the class/interface, we do + // need to validate the method constraints and signature are compatible + if (!bmtProp->fNoSanityChecks) + { + /////////////////////////////// + // Verify the signatures match + + MethodImplCompareSignatures( + hDecl, + hImpl, + FALSE /* allowCovariantReturn */, + IDS_CLASSLOAD_CONSTRAINT_MISMATCH_ON_INTERFACE_METHOD_IMPL); + + /////////////////////////////// + // Validate the method impl. + + TestMethodImpl(hDecl, hImpl); + } +} + //******************************************************************************* // This will validate that all interface methods that were matched during // layout also validate against type constraints. diff --git a/src/coreclr/vm/methodtablebuilder.h b/src/coreclr/vm/methodtablebuilder.h index c44568a..63cc9c8 100644 --- a/src/coreclr/vm/methodtablebuilder.h +++ b/src/coreclr/vm/methodtablebuilder.h @@ -1725,6 +1725,7 @@ private: : m_pType(pItfType), m_pImplTable(NULL), // Lazily created m_cImplTable(0), + m_cImplTableStatics(0), m_declScope(declScope), m_equivalenceSet(0), m_fEquivalenceSetWithMultipleEntries(false) @@ -1762,13 +1763,14 @@ private: typedef IteratorUtil::ArrayIterator InterfaceSlotIterator; + // Iterate the interface virtual methods that can be implemented. The static methods can be iterated if statics is set to true InterfaceSlotIterator IterateInterfaceSlots( - StackingAllocator * pStackingAllocator) + StackingAllocator * pStackingAllocator, bool statics = false) { WRAPPER_NO_CONTRACT; CheckCreateSlotTable(pStackingAllocator); - return InterfaceSlotIterator(m_pImplTable, m_cImplTable); + return InterfaceSlotIterator(m_pImplTable + (statics ? m_cImplTable : 0), statics ? m_cImplTableStatics : m_cImplTable); } //----------------------------------------------------------------------------------------- @@ -1850,6 +1852,7 @@ private: bmtRTType * m_pType; bmtInterfaceSlotImpl * m_pImplTable; SLOT_INDEX m_cImplTable; + SLOT_INDEX m_cImplTableStatics; InterfaceDeclarationScope m_declScope; UINT32 m_equivalenceSet; bool m_fEquivalenceSetWithMultipleEntries; @@ -2734,7 +2737,7 @@ private: // Find the decl method on a given interface entry that matches the method name+signature specified // If none is found, return a null method handle bmtMethodHandle - FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry, MethodSignature &declSig); + FindDeclMethodOnInterfaceEntry(bmtInterfaceEntry *pItfEntry, MethodSignature &declSig, bool searchForStaticMethods = false); // -------------------------------------------------------------------------------------------- // Find the decl method within the class hierarchy method name+signature specified @@ -2815,6 +2818,13 @@ private: DWORD dwMaxSlotSize); // -------------------------------------------------------------------------------------------- + // Validate that the methodimpl is handled correctly + VOID + ValidateStaticMethodImpl( + bmtMethodHandle hDecl, + bmtMethodHandle hImpl); + + // -------------------------------------------------------------------------------------------- // This will validate that all interface methods that were matched during // layout also validate against type constraints. VOID diff --git a/src/coreclr/vm/runtimehandles.cpp b/src/coreclr/vm/runtimehandles.cpp index d3c2153..736f367 100644 --- a/src/coreclr/vm/runtimehandles.cpp +++ b/src/coreclr/vm/runtimehandles.cpp @@ -1190,7 +1190,7 @@ MethodDesc* QCALLTYPE RuntimeTypeHandle::GetInterfaceMethodImplementation(QCall: if (pMD->IsStatic()) { - pResult = typeHandle.GetMethodTable()->ResolveVirtualStaticMethod(thOwnerOfMD.GetMethodTable(), pMD, /* allowNullResult */ TRUE, /* checkDuplicates*/ FALSE, /*allowVariantMatches */ TRUE); + pResult = typeHandle.GetMethodTable()->ResolveVirtualStaticMethod(thOwnerOfMD.GetMethodTable(), pMD, /* allowNullResult */ TRUE, /* verifyImplemented*/ FALSE, /*allowVariantMatches */ TRUE); } else { diff --git a/src/coreclr/vm/typedesc.cpp b/src/coreclr/vm/typedesc.cpp index 657170b..fbc6ff0 100644 --- a/src/coreclr/vm/typedesc.cpp +++ b/src/coreclr/vm/typedesc.cpp @@ -1922,7 +1922,7 @@ BOOL TypeVarTypeDesc::SatisfiesConstraints(SigTypeContext *pTypeContextOfConstra MethodDesc *pMD = it.GetMethodDesc(); if (pMD->IsVirtual() && pMD->IsStatic() && - !thElem.AsMethodTable()->ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* checkDuplicates */ TRUE)) + !thElem.AsMethodTable()->ResolveVirtualStaticMethod(pInterfaceMT, pMD, /* allowNullResult */ TRUE, /* verifyImplemented */ TRUE)) { virtualStaticResolutionCheckFailed = true; break; diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/CuriouslyRecurringGenericWithUnimplementedMethod.il b/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/CuriouslyRecurringGenericWithUnimplementedMethod.il new file mode 100644 index 0000000..e4a952e --- /dev/null +++ b/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/CuriouslyRecurringGenericWithUnimplementedMethod.il @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.assembly extern System.Console {} +.assembly extern mscorlib {} +.assembly extern System.Runtime {} +.assembly extern TypeHierarchyCommonCs {} +.assembly TypeHierarchyTest {} +.class interface public abstract auto ansi InterfaceScenario1`1 +{ + .method public newslot virtual abstract static int32 Method() cil managed noinlining + { + } // end of method Method +} // end of class InterfaceScenario1 + +.class public auto ansi BaseScenario1`1 + extends [System.Runtime]System.Object + implements class InterfaceScenario1`1> +{ +} // end of class BaseScenario1 + +.class public auto ansi TestEntrypoint + extends [System.Runtime]System.Object +{ + .method public static class [System.Runtime]System.Type Test_Scenario1() cil managed noinlining + { + ldtoken class BaseScenario1`1 + call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle) + ret + } // end of method Test_Scenario1 + .method public static int32 Main() cil managed noinlining + { + .entrypoint + .locals init (class [System.Runtime]System.Exception V_0) + .try { + call class [System.Runtime]System.Type TestEntrypoint::Test_Scenario1() + pop + ldstr "CuriouslyRecurringGenericWithUnimplementedMethod" + ldstr "TypeLoadException" + ldstr "Did not throw exception" + call void [TypeHierarchyCommonCs]Statics::CheckForFailure(string,string,string) + leave.s CuriouslyRecurringGenericWithUnimplementedMethodDone + } catch [System.Runtime]System.Exception { + stloc.0 + ldstr "CuriouslyRecurringGenericWithUnimplementedMethod" + ldstr "TypeLoadException" + ldloc.0 + callvirt instance class [System.Runtime]System.Type [System.Runtime]System.Exception::GetType() + callvirt instance string [System.Runtime]System.Reflection.MemberInfo::get_Name() + call void [TypeHierarchyCommonCs]Statics::CheckForFailure(string,string,string) + leave.s CuriouslyRecurringGenericWithUnimplementedMethodDone + } +CuriouslyRecurringGenericWithUnimplementedMethodDone: nop + + call int32 [TypeHierarchyCommonCs]Statics::ReportResults() + ret } // end of method Main +} // end of class TestEntrypoint diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/CuriouslyRecurringGenericWithUnimplementedMethod.ilproj b/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/CuriouslyRecurringGenericWithUnimplementedMethod.ilproj new file mode 100644 index 0000000..19680b9 --- /dev/null +++ b/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/CuriouslyRecurringGenericWithUnimplementedMethod.ilproj @@ -0,0 +1,12 @@ + + + Exe + + + Full + + + + + + diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/MethodConstraintMismatch.il b/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/MethodConstraintMismatch.il new file mode 100644 index 0000000..bc957e1 --- /dev/null +++ b/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/MethodConstraintMismatch.il @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +.assembly extern System.Console {} +.assembly extern mscorlib {} +.assembly extern System.Runtime {} +.assembly extern TypeHierarchyCommonCs {} +.assembly TypeHierarchyTest {} +.class interface public abstract auto ansi InterfaceScenario1 +{ + .method public newslot virtual abstract static int32 GenericMethod() cil managed noinlining + { + } // end of method Method +} // end of class InterfaceScenario1 + +.class public auto ansi BaseScenario1 + extends [System.Runtime]System.Object + implements InterfaceScenario1 +{ + .method public static int32 GenericMethod<(class InterfaceScenario1) U>() cil managed noinlining + { + .override method int32 InterfaceScenario1::GenericMethod<[1]>() + .locals init (int32 V_O) + ldloca.s 0 + initobj int32 + ldloc.0 + ret + } // end of method Method +} // end of class BaseScenario1 + +.class public auto ansi TestEntrypoint + extends [System.Runtime]System.Object +{ + .method public static class [System.Runtime]System.Type Test_Scenario1() cil managed noinlining + { + ldtoken BaseScenario1 + call class [System.Runtime]System.Type [System.Runtime]System.Type::GetTypeFromHandle(valuetype [System.Runtime]System.RuntimeTypeHandle) + ret + } // end of method Test_Scenario1 + .method public static int32 Main() cil managed noinlining + { + .entrypoint + .locals init (class [System.Runtime]System.Exception V_0) + .try { + call class [System.Runtime]System.Type TestEntrypoint::Test_Scenario1() + pop + ldstr "MethodConstraintMismatch" + ldstr "TypeLoadException" + ldstr "Did not throw exception" + call void [TypeHierarchyCommonCs]Statics::CheckForFailure(string,string,string) + leave.s MethodConstraintMismatchDone + } catch [System.Runtime]System.Exception { + stloc.0 + ldstr "MethodConstraintMismatch" + ldstr "TypeLoadException" + ldloc.0 + callvirt instance class [System.Runtime]System.Type [System.Runtime]System.Exception::GetType() + callvirt instance string [System.Runtime]System.Reflection.MemberInfo::get_Name() + call void [TypeHierarchyCommonCs]Statics::CheckForFailure(string,string,string) + leave.s MethodConstraintMismatchDone + } +MethodConstraintMismatchDone: nop + + call int32 [TypeHierarchyCommonCs]Statics::ReportResults() + ret } // end of method Main +} // end of class TestEntrypoint diff --git a/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/MethodConstraintMismatch.ilproj b/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/MethodConstraintMismatch.ilproj new file mode 100644 index 0000000..19680b9 --- /dev/null +++ b/src/tests/Loader/classloader/StaticVirtualMethods/NegativeTestCases/MethodConstraintMismatch.ilproj @@ -0,0 +1,12 @@ + + + Exe + + + Full + + + + + + -- 2.7.4