Skip to content

Commit cc03608

Browse files
Fix remaining failing test issues in current testbed (#13)
- Fix crashing issue where wrong instantiation parameter is passed - Instead use the CORINFO_CALL_CODE_POINTER infrastructure to simply use an exact function pointer - Additionally, my previous attempt to fix a different bug added the concept of allowing instantiation parameters to be required when using resolving the static virtual at compile time. In order to reliably use the CORINFO_CALL_CODE_POINTER infrastructure, that logic was deleted and removed. - Fix issue where the type resolved via ResolveVirtualStaticMethod was on a slighly different interface type - Fix by passing the interface type pointer to ResolveVirtualStaticMethod - And by performing exact interface type checks in TryResolveVirtualStaticMethodOnThisType - Finally there was 1 remaining bug in the test generator for the GenericContextTest where the correct string to test for was not generated - Fix by deleting a hack used earlier to get closer to passing - Drive by feature to verify that the MethodBody on a MethodImpl follows the rules around being a MethodDef and being implemented by the type that defines the MethodImpl.
1 parent de3852f commit cc03608

File tree

6 files changed

+130
-66
lines changed

6 files changed

+130
-66
lines changed

src/coreclr/vm/genericdict.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,11 +1382,11 @@ Dictionary::PopulateEntry(
13821382
}
13831383
_ASSERTE(!constraintType.IsNull());
13841384

1385-
MethodDesc *pResolvedMD = constraintType.GetMethodTable()->TryResolveConstraintMethodApprox(ownerType, pMethod, !pMethod->IsStatic());
1385+
MethodDesc *pResolvedMD = constraintType.GetMethodTable()->TryResolveConstraintMethodApprox(ownerType, pMethod);
13861386

13871387
// All such calls should be resolvable. If not then for now just throw an error.
13881388
_ASSERTE(pResolvedMD);
1389-
INDEBUG(if (!pResolvedMD) constraintType.GetMethodTable()->TryResolveConstraintMethodApprox(ownerType, pMethod, !pMethod->IsStatic());)
1389+
INDEBUG(if (!pResolvedMD) constraintType.GetMethodTable()->TryResolveConstraintMethodApprox(ownerType, pMethod);)
13901390
if (!pResolvedMD)
13911391
COMPlusThrowHR(COR_E_BADIMAGEFORMAT);
13921392

src/coreclr/vm/jitinterface.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5183,7 +5183,6 @@ void CEEInfo::getCallInfo(
51835183
MethodDesc * directMethod = constrainedType.GetMethodTable()->TryResolveConstraintMethodApprox(
51845184
exactType,
51855185
pMD,
5186-
!!(flags & CORINFO_CALLINFO_ALLOWINSTPARAM),
51875186
&fForceUseRuntimeLookup);
51885187
if (directMethod
51895188
#ifdef FEATURE_DEFAULT_INTERFACES
@@ -5355,6 +5354,29 @@ void CEEInfo::getCallInfo(
53555354

53565355
bool allowInstParam = (flags & CORINFO_CALLINFO_ALLOWINSTPARAM);
53575356

5357+
// If the target method is resolved via constrained static virtual dispatch
5358+
// And it requires an instParam, we do not have the generic dictionary infrastructure
5359+
// to load the correct generic context arg via EmbedGenericHandle.
5360+
// Instead, force the call to go down the CORINFO_CALL_CODE_POINTER code path
5361+
// which should have somewhat inferior performance. This should only actually happen in the case
5362+
// of shared generic code calling a shared generic implementation method, which should be rare.
5363+
//
5364+
// An alternative design would be to add a new generic dictionary entry kind to hold the MethodDesc
5365+
// of the constrained target instead, and use that in some circumstances; however, implementation of
5366+
// that design requires refactoring variuos parts of the JIT interface as well as
5367+
// TryResolveConstraintMethodApprox. In particular we would need to be abled to embed a constrained lookup
5368+
// via EmbedGenericHandle, as well as decide in TryResolveConstraintMethodApprox if the call can be made
5369+
// via a single use of CORINFO_CALL_CODE_POINTER, or would be better done with a CORINFO_CALL + embedded
5370+
// constrained generic handle, or if there is a case where we would want to use both a CORINFO_CALL and
5371+
// embedded constrained generic handle. Given the current expected high performance use case of this feature
5372+
// which is generic numerics which will always resolve to exact valuetypes, it is not expected that
5373+
// the complexity involved would be worth the risk. Other scenarios are not expected to be as performance
5374+
// sensitive.
5375+
if (IsMdStatic(dwTargetMethodAttrs) && constrainedType != NULL && pResult->exactContextNeedsRuntimeLookup)
5376+
{
5377+
allowInstParam = FALSE;
5378+
}
5379+
53585380
// Create instantiating stub if necesary
53595381
if (!allowInstParam && pTargetMD->RequiresInstArg())
53605382
{

src/coreclr/vm/methodtable.cpp

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9173,14 +9173,42 @@ MethodDesc *MethodTable::GetDefaultConstructor(BOOL forceBoxedEntryPoint /* = FA
91739173
//==========================================================================================
91749174
// Finds the (non-unboxing) MethodDesc that implements the interface virtual static method pInterfaceMD.
91759175
MethodDesc *
9176-
MethodTable::ResolveVirtualStaticMethod(MethodDesc* pInterfaceMD, BOOL allowInstParam, BOOL allowNullResult)
9176+
MethodTable::ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult)
91779177
{
9178-
for (MethodTable* pMT = this; pMT != nullptr; pMT = pMT->GetParentMethodTable())
9178+
if (!pInterfaceMD->IsSharedByGenericMethodInstantiations() && !pInterfaceType->IsSharedByGenericInstantiations())
91799179
{
9180-
MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceMD, allowInstParam);
9181-
if (pMD != nullptr)
9180+
// Check that there is no implementation of the interface on this type which is the canonical interface for a shared generic. If so, that indicates that
9181+
// we cannot exactly compute a target method result, as even if there is an exact match in the type hierarchy
9182+
// it isn't guaranteed that we will always find the right result, as we may find a match on a base type when we should find the match
9183+
// on a more derived type.
9184+
9185+
MethodTable *pInterfaceTypeCanonical = pInterfaceType->GetCanonicalMethodTable();
9186+
bool canonicalEquivalentFound = false;
9187+
if (pInterfaceType != pInterfaceTypeCanonical)
91829188
{
9183-
return pMD;
9189+
InterfaceMapIterator it = IterateInterfaceMap();
9190+
while (it.Next())
9191+
{
9192+
if (pInterfaceTypeCanonical == it.GetInterface())
9193+
{
9194+
canonicalEquivalentFound = true;
9195+
break;
9196+
return NULL;
9197+
}
9198+
}
9199+
}
9200+
9201+
if (!canonicalEquivalentFound)
9202+
{
9203+
// Search for match on a per-level in the type hierarchy
9204+
for (MethodTable* pMT = this; pMT != nullptr; pMT = pMT->GetParentMethodTable())
9205+
{
9206+
MethodDesc* pMD = pMT->TryResolveVirtualStaticMethodOnThisType(pInterfaceType, pInterfaceMD);
9207+
if (pMD != nullptr)
9208+
{
9209+
return pMD;
9210+
}
9211+
}
91849212
}
91859213
}
91869214

@@ -9194,7 +9222,7 @@ MethodTable::ResolveVirtualStaticMethod(MethodDesc* pInterfaceMD, BOOL allowInst
91949222
// Try to locate the appropriate MethodImpl matching a given interface static virtual method.
91959223
// Returns nullptr on failure.
91969224
MethodDesc*
9197-
MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodDesc* pInterfaceMD, BOOL allowInstParam)
9225+
MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD)
91989226
{
91999227
HRESULT hr = S_OK;
92009228
IMDInternalImport* pMDInternalImport = GetMDImport();
@@ -9210,6 +9238,8 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodDesc* pInterfaceMD, B
92109238
// This gets the count out of the metadata interface.
92119239
uint32_t dwNumberMethodImpls = hEnumMethodImpl.EnumMethodImplGetCount();
92129240

9241+
// TODO: support type-equivalent interface type matches and variant interface scenarios
9242+
92139243
// Iterate through each MethodImpl declared on this class
92149244
for (uint32_t i = 0; i < dwNumberMethodImpls; i++)
92159245
{
@@ -9237,8 +9267,7 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodDesc* pInterfaceMD, B
92379267
tkParent,
92389268
&sigTypeContext)
92399269
.GetMethodTable();
9240-
if (pInterfaceMT != pInterfaceMD->GetMethodTable() &&
9241-
pInterfaceMT->GetCanonicalMethodTable() != pInterfaceMD->GetMethodTable())
9270+
if (pInterfaceMT != pInterfaceType)
92429271
{
92439272
continue;
92449273
}
@@ -9256,6 +9285,13 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodDesc* pInterfaceMD, B
92569285
{
92579286
continue;
92589287
}
9288+
9289+
// Spec requires that all body token for MethodImpls that refer to static virtual implementation methods must be MethodDef tokens.
9290+
if (TypeFromToken(methodBody) != mdtMethodDef)
9291+
{
9292+
COMPlusThrow(kTypeLoadException, E_FAIL);
9293+
}
9294+
92599295
MethodDesc *pMethodImpl = MemberLoader::GetMethodDescFromMemberDefOrRefOrSpec(
92609296
GetModule(),
92619297
methodBody,
@@ -9267,9 +9303,16 @@ MethodTable::TryResolveVirtualStaticMethodOnThisType(MethodDesc* pInterfaceMD, B
92679303
COMPlusThrow(kTypeLoadException, E_FAIL);
92689304
}
92699305

9306+
// Spec requires that all body token for MethodImpls that refer to static virtual implementation methods must to methods
9307+
// defined on the same type that defines the MethodImpl
9308+
if (!HasSameTypeDefAs(pMethodImpl->GetMethodTable()))
9309+
{
9310+
COMPlusThrow(kTypeLoadException, E_FAIL);
9311+
}
9312+
92709313
if (pInterfaceMD->HasMethodInstantiation() || pMethodImpl->HasMethodInstantiation() || HasInstantiation())
92719314
{
9272-
return pMethodImpl->FindOrCreateAssociatedMethodDesc(pMethodImpl, this, FALSE, pInterfaceMD->GetMethodInstantiation(), allowInstParam);
9315+
return pMethodImpl->FindOrCreateAssociatedMethodDesc(pMethodImpl, this, FALSE, pInterfaceMD->GetMethodInstantiation(), /* allowInstParam */ FALSE);
92739316
}
92749317
else
92759318
{
@@ -9292,7 +9335,6 @@ MethodDesc *
92929335
MethodTable::TryResolveConstraintMethodApprox(
92939336
TypeHandle thInterfaceType,
92949337
MethodDesc * pInterfaceMD,
9295-
BOOL allowInstParam,
92969338
BOOL * pfForceUseRuntimeLookup) // = NULL
92979339
{
92989340
CONTRACTL {
@@ -9302,7 +9344,9 @@ MethodTable::TryResolveConstraintMethodApprox(
93029344

93039345
if (pInterfaceMD->IsStatic())
93049346
{
9305-
MethodDesc *result = ResolveVirtualStaticMethod(pInterfaceMD, allowInstParam, pfForceUseRuntimeLookup != NULL);
9347+
_ASSERTE(!thInterfaceType.IsTypeDesc());
9348+
_ASSERTE(thInterfaceType.IsInterface());
9349+
MethodDesc *result = ResolveVirtualStaticMethod(thInterfaceType.GetMethodTable(), pInterfaceMD, pfForceUseRuntimeLookup != NULL);
93069350
if (result == NULL)
93079351
{
93089352
*pfForceUseRuntimeLookup = TRUE;

src/coreclr/vm/methodtable.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2280,7 +2280,7 @@ class MethodTable
22802280

22812281

22822282
// Resolve virtual static interface method pInterfaceMD on this type.
2283-
MethodDesc *ResolveVirtualStaticMethod(MethodDesc* pInterfaceMD, BOOL allowInstParam, BOOL allowNullResult);
2283+
MethodDesc *ResolveVirtualStaticMethod(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD, BOOL allowNullResult);
22842284

22852285
// Try a partial resolve of the constraint call, up to generic code sharing.
22862286
//
@@ -2297,7 +2297,6 @@ class MethodTable
22972297
MethodDesc * TryResolveConstraintMethodApprox(
22982298
TypeHandle ownerType,
22992299
MethodDesc * pMD,
2300-
BOOL fExactResolution = FALSE,
23012300
BOOL * pfForceUseRuntimeLookup = NULL);
23022301

23032302
//-------------------------------------------------------------------
@@ -2398,7 +2397,7 @@ class MethodTable
23982397

23992398
// Try to resolve a given static virtual method override on this type. Return nullptr
24002399
// when not found.
2401-
MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodDesc* pInterfaceMD, BOOL allowInstParam);
2400+
MethodDesc *TryResolveVirtualStaticMethodOnThisType(MethodTable* pInterfaceType, MethodDesc* pInterfaceMD);
24022401

24032402
public:
24042403
static MethodDesc *MapMethodDeclToMethodImpl(MethodDesc *pMDDecl);

src/tests/Loader/classloader/StaticVirtualMethods/GenericContext/Generator/Program.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,6 @@ static void Main(string[] args)
532532

533533
string expectedString = constrainedTypePrefix + AppendSuffixToConstrainedType(scenario, GetConstrainedTypeName(scenario.ConstrainedTypeDefinition, withImplPrefix: false), out _) + "'" + interfaceTypeSansImplPrefix + "." + interfaceMethodRoot + "'" + interfaceMethodInstantiation;
534534
expectedString = expectedString.Replace(ImplPrefix, "");
535-
expectedString = expectedString.Replace("<class GenericClass`1<!!0>>", "<class GenericClass`1<!0>>");
536535

537536
if (scenario.CallerScenario == CallerMethodScenario.NonGeneric)
538537
{

0 commit comments

Comments
 (0)