Skip to content

Commit 3a8b089

Browse files
committed
GetInterfaceMap
1 parent 98aa284 commit 3a8b089

File tree

3 files changed

+84
-2
lines changed

3 files changed

+84
-2
lines changed

src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2736,14 +2736,21 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn
27362736
im.InterfaceMethods = new MethodInfo[ifaceVirtualMethodCount];
27372737
im.TargetMethods = new MethodInfo[ifaceVirtualMethodCount];
27382738

2739+
int actualCount = 0;
27392740
for (int i = 0; i < ifaceVirtualMethodCount; i++)
27402741
{
27412742
RuntimeMethodHandleInternal ifaceRtMethodHandle = RuntimeTypeHandle.GetMethodAt(ifaceRtType, i);
27422743

2744+
// GetMethodAt may return null handle for methods that do not exist or are not supposed
2745+
// to be seen in reflection.
2746+
// One example is async variant methods.
2747+
if (ifaceRtMethodHandle.IsNullHandle())
2748+
continue;
2749+
27432750
// GetMethodBase will convert this to the instantiating/unboxing stub if necessary
27442751
MethodBase ifaceMethodBase = GetMethodBase(ifaceRtType, ifaceRtMethodHandle)!;
27452752
Debug.Assert(ifaceMethodBase is RuntimeMethodInfo);
2746-
im.InterfaceMethods[i] = (MethodInfo)ifaceMethodBase;
2753+
im.InterfaceMethods[actualCount] = (MethodInfo)ifaceMethodBase;
27472754

27482755
// If the impl is null, then virtual stub dispatch is active.
27492756
RuntimeMethodHandleInternal classRtMethodHandle = TypeHandle.GetInterfaceMethodImplementation(ifaceRtTypeHandle, ifaceRtMethodHandle);
@@ -2764,8 +2771,16 @@ public override InterfaceMapping GetInterfaceMap([DynamicallyAccessedMembers(Dyn
27642771
// the TargetMethod provided to us by runtime internals may be a generic method instance,
27652772
// potentially with invalid arguments. TargetMethods in the InterfaceMap should never be
27662773
// instances, only definitions.
2767-
im.TargetMethods[i] = (targetMethod is { IsGenericMethod: true, IsGenericMethodDefinition: false })
2774+
im.TargetMethods[actualCount] = (targetMethod is { IsGenericMethod: true, IsGenericMethodDefinition: false })
27682775
? targetMethod.GetGenericMethodDefinition() : targetMethod!;
2776+
2777+
actualCount++;
2778+
}
2779+
2780+
if (actualCount != ifaceVirtualMethodCount)
2781+
{
2782+
Array.Resize(ref im.InterfaceMethods, actualCount);
2783+
Array.Resize(ref im.TargetMethods, actualCount);
27692784
}
27702785

27712786
return im;

src/coreclr/vm/runtimehandles.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,15 @@ extern "C" MethodDesc* QCALLTYPE RuntimeTypeHandle_GetMethodAt(MethodTable* pMT,
443443

444444
END_QCALL;
445445

446+
if (pRetMethod != NULL && pRetMethod->IsAsyncVariantMethod())
447+
{
448+
// do not return methoddescs for async variants.
449+
// NOTE: The only scenario where this is relevant is when caller iterates through all slots.
450+
// If GetMethodAt is used to find a method associated with another one,
451+
// then we would be starting with "real" method and will get a "real" method here.
452+
return NULL;
453+
}
454+
446455
return pRetMethod;
447456
}
448457

src/tests/async/reflection/reflection-simple.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,62 @@ static async Task AwaitF<T>(T expected, Func<Task<T>> f)
6969
var res = await f.Invoke();
7070
Assert.Equal(expected, res);
7171
}
72+
73+
public interface IExample<T>
74+
{
75+
Task TaskReturning();
76+
T TReturning();
77+
}
78+
79+
public class ExampleClass : IExample<Task>
80+
{
81+
public Task TaskReturning()
82+
{
83+
return null;
84+
}
85+
86+
public Task TReturning()
87+
{
88+
return null;
89+
}
90+
}
91+
92+
public struct ExampleStruct : IExample<Task>
93+
{
94+
public Task TaskReturning()
95+
{
96+
return null;
97+
}
98+
99+
public Task TReturning()
100+
{
101+
return null;
102+
}
103+
}
104+
105+
[Fact]
106+
public static void GetInterfaceMap()
107+
{
108+
Type interfaceType = typeof(IExample<Task>);
109+
Type classType = typeof(ExampleClass);
110+
111+
InterfaceMapping map = classType.GetInterfaceMap(interfaceType);
112+
113+
Assert.Equal(2, map.InterfaceMethods.Length);
114+
Assert.Equal("System.Threading.Tasks.Task TaskReturning() --> System.Threading.Tasks.Task TaskReturning()",
115+
$"{map.InterfaceMethods[0]?.ToString()} --> {map.TargetMethods[0]?.ToString()}");
116+
117+
Assert.Equal("System.Threading.Tasks.Task TReturning() --> System.Threading.Tasks.Task TReturning()",
118+
$"{map.InterfaceMethods[1]?.ToString()} --> {map.TargetMethods[1]?.ToString()}");
119+
120+
Type structType = typeof(ExampleStruct);
121+
122+
map = structType.GetInterfaceMap(interfaceType);
123+
Assert.Equal(2, map.InterfaceMethods.Length);
124+
Assert.Equal("System.Threading.Tasks.Task TaskReturning() --> System.Threading.Tasks.Task TaskReturning()",
125+
$"{map.InterfaceMethods[0]?.ToString()} --> {map.TargetMethods[0]?.ToString()}");
126+
127+
Assert.Equal("System.Threading.Tasks.Task TReturning() --> System.Threading.Tasks.Task TReturning()",
128+
$"{map.InterfaceMethods[1]?.ToString()} --> {map.TargetMethods[1]?.ToString()}");
129+
}
72130
}

0 commit comments

Comments
 (0)