Skip to content

Commit 17bae6c

Browse files
hamarb123jkotas
andauthored
Add tests for GetInterfaceMap on static interface methods (#90518)
* Add unit tests for #90351 * Add [ActiveIssue] to skip over failing test case * Add some more DIM test cases • Would be good to test these anyway • Added to see if the failures on mono are with static interface methods only, or to do with default implementation methods * Fix added DIM tests * Disable some of the tests for mono • Haven't opened an issue for this yet - will follow up with an issue & commit to add the issue link * Update src/libraries/System.Runtime/tests/System/Type/TypeTests.cs * Update TypeTests.cs * Add test number * Fix native AOT compiler crash --------- Co-authored-by: Jan Kotas <[email protected]>
1 parent 617f2e5 commit 17bae6c

File tree

2 files changed

+303
-2
lines changed

2 files changed

+303
-2
lines changed

src/coreclr/tools/Common/Compiler/GenericCycleDetection/GraphBuilder.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,10 @@ private void LookForVirtualOverrides(EcmaMethod method)
126126
|| interfaceMethod.Signature.IsStatic != method.Signature.IsStatic)
127127
continue;
128128

129-
MethodDesc impl = methodOwningType.ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod)?.GetMethodDefinition();
130-
if (impl == method)
129+
MethodDesc impl = interfaceMethod.Signature.IsStatic ?
130+
methodOwningType.ResolveInterfaceMethodToStaticVirtualMethodOnType(interfaceMethod) :
131+
methodOwningType.ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod);
132+
if (impl?.GetMethodDefinition() == method)
131133
{
132134
RecordBinding(this, interfaceMethod.Instantiation, method.Instantiation);
133135
// Continue the loop in case this method implements multiple interfaces

src/libraries/System.Runtime/tests/System/Type/TypeTests.cs

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,6 +1053,24 @@ public static IEnumerable<object[]> GetInterfaceMap_TestData()
10531053
}
10541054
};
10551055
yield return new object[]
1056+
{
1057+
typeof(DIMs.I2),
1058+
typeof(DIMs.C3),
1059+
new Tuple<MethodInfo, MethodInfo>[]
1060+
{
1061+
new Tuple<MethodInfo, MethodInfo>(typeof(DIMs.I2).GetMethod("System.Tests.TypeTestsExtended.DIMs.I1.M", BindingFlags.Instance | BindingFlags.NonPublic), null)
1062+
}
1063+
};
1064+
yield return new object[]
1065+
{
1066+
typeof(DIMs.I1),
1067+
typeof(DIMs.C3),
1068+
new Tuple<MethodInfo, MethodInfo>[]
1069+
{
1070+
new Tuple<MethodInfo, MethodInfo>(typeof(DIMs.I1).GetMethod("M"), typeof(DIMs.I3).GetMethod("System.Tests.TypeTestsExtended.DIMs.I1.M", BindingFlags.Instance | BindingFlags.NonPublic))
1071+
}
1072+
};
1073+
yield return new object[]
10561074
{
10571075
typeof(DIMs.I4),
10581076
typeof(DIMs.C4),
@@ -1062,6 +1080,15 @@ public static IEnumerable<object[]> GetInterfaceMap_TestData()
10621080
}
10631081
};
10641082
yield return new object[]
1083+
{
1084+
typeof(DIMs.I3),
1085+
typeof(DIMs.C4),
1086+
new Tuple<MethodInfo, MethodInfo>[]
1087+
{
1088+
new Tuple<MethodInfo, MethodInfo>(typeof(DIMs.I3).GetMethod("System.Tests.TypeTestsExtended.DIMs.I1.M", BindingFlags.Instance | BindingFlags.NonPublic), typeof(DIMs.I3).GetMethod("System.Tests.TypeTestsExtended.DIMs.I1.M", BindingFlags.Instance | BindingFlags.NonPublic))
1089+
}
1090+
};
1091+
yield return new object[]
10651092
{
10661093
typeof(DIMs.I2),
10671094
typeof(DIMs.C4),
@@ -1070,6 +1097,148 @@ public static IEnumerable<object[]> GetInterfaceMap_TestData()
10701097
new Tuple<MethodInfo, MethodInfo>(typeof(DIMs.I2).GetMethod("System.Tests.TypeTestsExtended.DIMs.I1.M", BindingFlags.Instance | BindingFlags.NonPublic), null)
10711098
}
10721099
};
1100+
yield return new object[]
1101+
{
1102+
typeof(DIMs.I1),
1103+
typeof(DIMs.C4),
1104+
new Tuple<MethodInfo, MethodInfo>[]
1105+
{
1106+
new Tuple<MethodInfo, MethodInfo>(typeof(DIMs.I1).GetMethod("M"), typeof(DIMs.C4).GetMethod("M"))
1107+
}
1108+
};
1109+
1110+
// Test all combinations of the following:
1111+
// Static method
1112+
// Implementation by having the same name, explicit implementation, & default implementation (where applicable - only level 2)
1113+
// Non-generic interface, generic interface
1114+
// Non-generic type, generic type
1115+
// 3 levels of inheritance (of the interfaces): 1 - static abstract method, 2 - add a default implementation, 3 - re-abstractify it
1116+
// Checks that all the applicable interfaces are working properly
1117+
(Type Type, bool IncludePrefix, bool AnyTarget, Type InterfaceGenericParameter, int Index)[] classTypes = new (Type, bool, bool, Type, int)[]
1118+
{
1119+
// List of every type we are going to test
1120+
// (Type, whether it's implemented explicitly, whether it's not implemented in the level 2 interface, the generic parameter for Ix<T>, the level)
1121+
(typeof(SIMs.C1), false, true, typeof(int), 1),
1122+
(typeof(SIMs.C1Explicit), true, true, typeof(int), 1),
1123+
(typeof(SIMs.C1<string>), false, true, typeof(string), 1),
1124+
(typeof(SIMs.C1Explicit<string>), true, true, typeof(string), 1),
1125+
(typeof(SIMs.C1<>), false, true, typeof(SIMs.C1<>).GetGenericArguments()[0], 1),
1126+
(typeof(SIMs.C1Explicit<>), true, true, typeof(SIMs.C1Explicit<>).GetGenericArguments()[0], 1),
1127+
(typeof(SIMs.C2Implicit), false, false, typeof(int), 2),
1128+
(typeof(SIMs.C2), false, true, typeof(int), 2),
1129+
(typeof(SIMs.C2Explicit), true, true, typeof(int), 2),
1130+
(typeof(SIMs.C2Implicit<string>), false, false, typeof(string), 2),
1131+
(typeof(SIMs.C2<string>), false, true, typeof(string), 2),
1132+
(typeof(SIMs.C2Explicit<string>), true, true, typeof(string), 2),
1133+
(typeof(SIMs.C2Implicit<>), false, false, typeof(SIMs.C2Implicit<>).GetGenericArguments()[0], 2),
1134+
(typeof(SIMs.C2<>), false, true, typeof(SIMs.C2<>).GetGenericArguments()[0], 2),
1135+
(typeof(SIMs.C2Explicit<>), true, true, typeof(SIMs.C2Explicit<>).GetGenericArguments()[0], 2),
1136+
(typeof(SIMs.C3), false, true, typeof(int), 3),
1137+
(typeof(SIMs.C3Explicit), true, true, typeof(int), 3),
1138+
(typeof(SIMs.C3<string>), false, true, typeof(string), 3),
1139+
(typeof(SIMs.C3Explicit<string>), true, true, typeof(string), 3),
1140+
(typeof(SIMs.C3<>), false, true, typeof(SIMs.C3<>).GetGenericArguments()[0], 3),
1141+
(typeof(SIMs.C3Explicit<>), true, true, typeof(SIMs.C3Explicit<>).GetGenericArguments()[0], 3),
1142+
};
1143+
foreach ((Type Type, bool IncludePrefix, bool AnyTarget, Type InterfaceGenericParameter, int Index) classType in classTypes)
1144+
{
1145+
BindingFlags bindingFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
1146+
1147+
// This is the member name of the explicit interface implementation method in the class for the generic interface
1148+
string level1MethodNamePrefixTyped = "System.Tests.TypeTestsExtended.SIMs.I1<" + (classType.Type.GetGenericArguments().Length == 0 ? "System.Int32" : "S") + ">.";
1149+
1150+
// Check we have the expected implementation for the level 1 interfaces (abstract definitions - M and G methods)
1151+
Type level1GenericInterface = typeof(SIMs.I1<>).MakeGenericType(classType.InterfaceGenericParameter);
1152+
Type level2GenericInterface = typeof(SIMs.I2<>).MakeGenericType(classType.InterfaceGenericParameter);
1153+
Type level3GenericInterface = typeof(SIMs.I3<>).MakeGenericType(classType.InterfaceGenericParameter);
1154+
foreach ((Type Type, Type Level2InterfaceType, string MethodNamePrefix, string MethodNamePrefixTyped) interfaceType in new (Type, Type, string, string)[]
1155+
{
1156+
(typeof(SIMs.I1), typeof(SIMs.I2), "System.Tests.TypeTestsExtended.SIMs.I1.", "System.Tests.TypeTestsExtended.SIMs.I1."),
1157+
(level1GenericInterface, level2GenericInterface, "System.Tests.TypeTestsExtended.SIMs.I1<S>.", level1MethodNamePrefixTyped),
1158+
})
1159+
{
1160+
// Look up the interface method which should be implemented
1161+
MethodInfo MInterface = interfaceType.Type.GetMethod("M", bindingFlags);
1162+
MethodInfo GInterface = interfaceType.Type.GetMethod("G", bindingFlags);
1163+
1164+
// Look up the implementation
1165+
MethodInfo MTarget, GTarget;
1166+
if (classType.AnyTarget)
1167+
{
1168+
// The class implements it, either implicitly or explicitly (if IncludePrefix is specified)
1169+
MTarget = classType.Type.GetMethod((classType.IncludePrefix ? interfaceType.MethodNamePrefixTyped : "") + "M", bindingFlags);
1170+
GTarget = classType.Type.GetMethod((classType.IncludePrefix ? interfaceType.MethodNamePrefixTyped : "") + "G", bindingFlags);
1171+
}
1172+
else
1173+
{
1174+
// [ActiveIssue("https://github.com/dotnet/runtime/issues/90863")]
1175+
if (classType.Type == typeof(SIMs.C2Implicit<string>) && interfaceType.Type == typeof(SIMs.I1<string>)) continue;
1176+
1177+
// It's implemented implicitly by the level 2 interface
1178+
MTarget = interfaceType.Level2InterfaceType.GetMethod(interfaceType.MethodNamePrefix + "M", bindingFlags);
1179+
GTarget = interfaceType.Level2InterfaceType.GetMethod(interfaceType.MethodNamePrefix + "G", bindingFlags);
1180+
}
1181+
1182+
// Return our test case
1183+
yield return new object[]
1184+
{
1185+
interfaceType.Type,
1186+
classType.Type,
1187+
new Tuple<MethodInfo, MethodInfo>[]
1188+
{
1189+
new Tuple<MethodInfo, MethodInfo>(MInterface, MTarget),
1190+
new Tuple<MethodInfo, MethodInfo>(GInterface, GTarget)
1191+
}
1192+
};
1193+
}
1194+
1195+
// Check we have the expected implementation for the level 2 interfaces (virtual explicit default implementations - none)
1196+
if (classType.Index >= 2)
1197+
{
1198+
// There should be no methods for these interfaces
1199+
// Return our test cases
1200+
yield return new object[]
1201+
{
1202+
typeof(SIMs.I2),
1203+
classType.Type,
1204+
new Tuple<MethodInfo, MethodInfo>[0]
1205+
};
1206+
yield return new object[]
1207+
{
1208+
level2GenericInterface,
1209+
classType.Type,
1210+
new Tuple<MethodInfo, MethodInfo>[0]
1211+
};
1212+
}
1213+
1214+
// Check we have the expected implementation for the level 3 interfaces (abstract explicit implementations - I1.M and I1.G methods)
1215+
// Fails on mono: [ActiveIssue("https://github.com/dotnet/runtime/issues/91027")]
1216+
if (!PlatformDetection.IsMonoRuntime && classType.Index >= 3)
1217+
{
1218+
foreach ((Type Type, string MethodNamePrefix) interfaceType in new (Type, string)[]
1219+
{
1220+
(typeof(SIMs.I3), "System.Tests.TypeTestsExtended.SIMs.I1."),
1221+
(level3GenericInterface, "System.Tests.TypeTestsExtended.SIMs.I1<S>."),
1222+
})
1223+
{
1224+
// There should be no implementation for these methods - null
1225+
MethodInfo MInterface = interfaceType.Type.GetMethod(interfaceType.MethodNamePrefix + "M", bindingFlags);
1226+
MethodInfo GInterface = interfaceType.Type.GetMethod(interfaceType.MethodNamePrefix + "G", bindingFlags);
1227+
1228+
// Return our test cases
1229+
yield return new object[]
1230+
{
1231+
interfaceType.Type,
1232+
classType.Type,
1233+
new Tuple<MethodInfo, MethodInfo>[]
1234+
{
1235+
new Tuple<MethodInfo, MethodInfo>(MInterface, null),
1236+
new Tuple<MethodInfo, MethodInfo>(GInterface, null)
1237+
}
1238+
};
1239+
}
1240+
}
1241+
}
10731242
}
10741243

10751244
[ActiveIssue("https://github.com/dotnet/runtime/issues/89157", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))]
@@ -1167,6 +1336,136 @@ internal abstract class C4 : I4
11671336
public abstract void M();
11681337
}
11691338
}
1339+
1340+
static class SIMs
1341+
{
1342+
internal interface I1
1343+
{
1344+
static abstract void M();
1345+
static abstract void G<T>();
1346+
}
1347+
internal interface I1<S>
1348+
{
1349+
static abstract void M();
1350+
static abstract void G<T>();
1351+
}
1352+
1353+
internal class C1 : I1, I1<int>
1354+
{
1355+
public static void M() { }
1356+
public static void G<T>() { }
1357+
}
1358+
1359+
internal class C1Explicit : I1, I1<int>
1360+
{
1361+
static void I1.M() { }
1362+
static void I1.G<T>() { }
1363+
static void I1<int>.M() { }
1364+
static void I1<int>.G<T>() { }
1365+
}
1366+
1367+
internal class C1<S> : I1, I1<S>
1368+
{
1369+
public static void M() { }
1370+
public static void G<T>() { }
1371+
}
1372+
1373+
internal class C1Explicit<S> : I1, I1<S>
1374+
{
1375+
static void I1.M() { }
1376+
static void I1.G<T>() { }
1377+
static void I1<S>.M() { }
1378+
static void I1<S>.G<T>() { }
1379+
}
1380+
1381+
1382+
internal interface I2 : I1
1383+
{
1384+
// add a default implementation
1385+
static void I1.M() { }
1386+
static void I1.G<T>() { }
1387+
}
1388+
internal interface I2<S> : I1<S>
1389+
{
1390+
// add a default implementation
1391+
static void I1<S>.M() { }
1392+
static void I1<S>.G<T>() { }
1393+
}
1394+
1395+
internal class C2Implicit : I2, I2<int> { }
1396+
1397+
internal class C2 : I2, I2<int>
1398+
{
1399+
public static void M() { }
1400+
public static void G<T>() { }
1401+
}
1402+
1403+
internal class C2Explicit : I2, I2<int>
1404+
{
1405+
static void I1.M() { }
1406+
static void I1.G<T>() { }
1407+
static void I1<int>.M() { }
1408+
static void I1<int>.G<T>() { }
1409+
}
1410+
1411+
internal class C2Implicit<S> : I2, I2<S> { }
1412+
1413+
internal class C2<S> : I2, I2<S>
1414+
{
1415+
public static void M() { }
1416+
public static void G<T>() { }
1417+
}
1418+
1419+
internal class C2Explicit<S> : I2, I2<S>
1420+
{
1421+
static void I1.M() { }
1422+
static void I1.G<T>() { }
1423+
static void I1<S>.M() { }
1424+
static void I1<S>.G<T>() { }
1425+
}
1426+
1427+
1428+
internal interface I3 : I2
1429+
{
1430+
// reabstract it
1431+
static abstract void I1.M();
1432+
static abstract void I1.G<T>();
1433+
}
1434+
internal interface I3<S> : I2<S>
1435+
{
1436+
// reabstract it
1437+
static abstract void I1<S>.M();
1438+
static abstract void I1<S>.G<T>();
1439+
}
1440+
1441+
internal class C3 : I3, I3<int>
1442+
{
1443+
public static void M() { }
1444+
public static void G<T>() { }
1445+
}
1446+
1447+
internal class C3Explicit : I3, I3<int>
1448+
{
1449+
static void I1.M() { }
1450+
static void I1.G<T>() { }
1451+
static void I1<int>.M() { }
1452+
static void I1<int>.G<T>() { }
1453+
}
1454+
1455+
internal class C3<S> : I3, I3<S>
1456+
{
1457+
public static void M() { }
1458+
public static void G<T>() { }
1459+
}
1460+
1461+
internal class C3Explicit<S> : I3, I3<S>
1462+
{
1463+
static void I1.M() { }
1464+
static void I1.G<T>() { }
1465+
static void I1<S>.M() { }
1466+
static void I1<S>.G<T>() { }
1467+
}
1468+
}
11701469
#endregion
11711470

11721471
[Fact]

0 commit comments

Comments
 (0)