Fix reflection to work with default interface methods (#16034)
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>
Mon, 29 Jan 2018 16:37:27 +0000 (17:37 +0100)
committerGitHub <noreply@github.com>
Mon, 29 Jan 2018 16:37:27 +0000 (17:37 +0100)
Fixes #15645.
Fixes #15644.

13 files changed:
src/mscorlib/src/System/RtType.cs
src/mscorlib/src/System/RuntimeHandles.cs
src/vm/ecalllist.h
src/vm/runtimehandles.cpp
src/vm/runtimehandles.h
tests/src/reflection/DefaultInterfaceMethods/GetInterfaceMapConsumer.cs [new file with mode: 0644]
tests/src/reflection/DefaultInterfaceMethods/GetInterfaceMapConsumer.csproj [new file with mode: 0644]
tests/src/reflection/DefaultInterfaceMethods/GetInterfaceMapProvider.il [new file with mode: 0644]
tests/src/reflection/DefaultInterfaceMethods/GetInterfaceMapProvider.ilproj [new file with mode: 0644]
tests/src/reflection/DefaultInterfaceMethods/InvokeConsumer.cs [new file with mode: 0644]
tests/src/reflection/DefaultInterfaceMethods/InvokeConsumer.csproj [new file with mode: 0644]
tests/src/reflection/DefaultInterfaceMethods/InvokeProvider.il [new file with mode: 0644]
tests/src/reflection/DefaultInterfaceMethods/InvokeProvider.ilproj [new file with mode: 0644]

index 6873dd1..611e11d 100644 (file)
@@ -580,26 +580,25 @@ namespace System
 
                             #region Loop through all methods on the interface
                             Debug.Assert(!methodHandle.IsNullHandle());
-                            // Except for .ctor, .cctor, IL_STUB*, and static methods, all interface methods should be abstract, virtual, and non-RTSpecialName.
-                            // Note that this assumption will become invalid when we add support for non-abstract or static methods on interfaces.
+
+                            MethodAttributes methodAttributes = RuntimeMethodHandle.GetAttributes(methodHandle);
+
+                            #region Continue if this is a constructor
                             Debug.Assert(
-                                (RuntimeMethodHandle.GetAttributes(methodHandle) & (MethodAttributes.RTSpecialName | MethodAttributes.Abstract | MethodAttributes.Virtual)) == (MethodAttributes.Abstract | MethodAttributes.Virtual) ||
-                                (RuntimeMethodHandle.GetAttributes(methodHandle) & MethodAttributes.Static) == MethodAttributes.Static ||
-                                RuntimeMethodHandle.GetName(methodHandle).Equals(".ctor") ||
-                                RuntimeMethodHandle.GetName(methodHandle).Equals(".cctor") ||
-                                RuntimeMethodHandle.GetName(methodHandle).StartsWith("IL_STUB", StringComparison.Ordinal));
+                                (RuntimeMethodHandle.GetAttributes(methodHandle) & MethodAttributes.RTSpecialName) == 0 ||
+                                RuntimeMethodHandle.GetName(methodHandle).Equals(".cctor"));
+
+                            if ((methodAttributes & MethodAttributes.RTSpecialName) != 0)
+                                continue;
+                            #endregion
 
                             #region Calculate Binding Flags
-                            MethodAttributes methodAttributes = RuntimeMethodHandle.GetAttributes(methodHandle);
                             bool isPublic = (methodAttributes & MethodAttributes.MemberAccessMask) == MethodAttributes.Public;
                             bool isStatic = (methodAttributes & MethodAttributes.Static) != 0;
                             bool isInherited = false;
                             BindingFlags bindingFlags = RuntimeType.FilterPreCalculate(isPublic, isInherited, isStatic);
                             #endregion
 
-                            if ((methodAttributes & MethodAttributes.RTSpecialName) != 0)
-                                continue;
-
                             // get the unboxing stub or instantiating stub if needed
                             RuntimeMethodHandleInternal instantiatedHandle = RuntimeMethodHandle.GetStubIfNeeded(methodHandle, declaringType, null);
 
@@ -2747,15 +2746,19 @@ namespace System
                 Debug.Assert(ifaceMethodBase is RuntimeMethodInfo);
                 im.InterfaceMethods[i] = (MethodInfo)ifaceMethodBase;
 
-                // If the slot is -1, then virtual stub dispatch is active.
-                int slot = GetTypeHandleInternal().GetInterfaceMethodImplementationSlot(ifaceRtTypeHandle, ifaceRtMethodHandle);
+                // If the impl is null, then virtual stub dispatch is active.
+                RuntimeMethodHandleInternal classRtMethodHandle = GetTypeHandleInternal().GetInterfaceMethodImplementation(ifaceRtTypeHandle, ifaceRtMethodHandle);
 
-                if (slot == -1) continue;
+                if (classRtMethodHandle.IsNullHandle())
+                    continue;
 
-                RuntimeMethodHandleInternal classRtMethodHandle = RuntimeTypeHandle.GetMethodAt(this, slot);
+                // If we resolved to an interface method, use the interface type as reflected type. Otherwise use `this`.
+                RuntimeType reflectedType = RuntimeMethodHandle.GetDeclaringType(classRtMethodHandle);
+                if (!reflectedType.IsInterface)
+                    reflectedType = this;
 
                 // GetMethodBase will convert this to the instantiating/unboxing stub if necessary
-                MethodBase rtTypeMethodBase = RuntimeType.GetMethodBase(this, classRtMethodHandle);
+                MethodBase rtTypeMethodBase = RuntimeType.GetMethodBase(reflectedType, classRtMethodHandle);
                 // a class may not implement all the methods of an interface (abstract class) so null is a valid value 
                 Debug.Assert(rtTypeMethodBase == null || rtTypeMethodBase is RuntimeMethodInfo);
                 im.TargetMethods[i] = (MethodInfo)rtTypeMethodBase;
index dc84976..8dca7fd 100644 (file)
@@ -358,11 +358,11 @@ namespace System
         }
 
         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
-        private extern static int GetInterfaceMethodImplementationSlot(RuntimeTypeHandle handle, RuntimeTypeHandle interfaceHandle, RuntimeMethodHandleInternal interfaceMethodHandle);
+        private extern static RuntimeMethodHandleInternal GetInterfaceMethodImplementation(RuntimeTypeHandle handle, RuntimeTypeHandle interfaceHandle, RuntimeMethodHandleInternal interfaceMethodHandle);
 
-        internal int GetInterfaceMethodImplementationSlot(RuntimeTypeHandle interfaceHandle, RuntimeMethodHandleInternal interfaceMethodHandle)
+        internal RuntimeMethodHandleInternal GetInterfaceMethodImplementation(RuntimeTypeHandle interfaceHandle, RuntimeMethodHandleInternal interfaceMethodHandle)
         {
-            return GetInterfaceMethodImplementationSlot(GetNativeHandle(), interfaceHandle.GetNativeHandle(), interfaceMethodHandle);
+            return GetInterfaceMethodImplementation(GetNativeHandle(), interfaceHandle.GetNativeHandle(), interfaceMethodHandle);
         }
 
         [MethodImplAttribute(MethodImplOptions.InternalCall)]
index 525227b..8fbfd20 100644 (file)
@@ -267,7 +267,7 @@ FCFuncStart(gCOMTypeHandleFuncs)
     FCFuncElement("_GetMetadataImport", RuntimeTypeHandle::GetMetadataImport)
     FCFuncElement("GetNumVirtuals", RuntimeTypeHandle::GetNumVirtuals)
     QCFuncElement("VerifyInterfaceIsImplemented", RuntimeTypeHandle::VerifyInterfaceIsImplemented)
-    QCFuncElement("GetInterfaceMethodImplementationSlot", RuntimeTypeHandle::GetInterfaceMethodImplementationSlot)
+    QCFuncElement("GetInterfaceMethodImplementation", RuntimeTypeHandle::GetInterfaceMethodImplementation)
     FCFuncElement("IsComObject", RuntimeTypeHandle::IsComObject)
     FCFuncElement("IsValueType", RuntimeTypeHandle::IsValueType)
     FCFuncElement("IsInterface", RuntimeTypeHandle::IsInterface)
index 952be62..723222e 100644 (file)
@@ -1181,11 +1181,11 @@ void QCALLTYPE RuntimeTypeHandle::VerifyInterfaceIsImplemented(EnregisteredTypeH
     END_QCALL;
 }
 
-INT32 QCALLTYPE RuntimeTypeHandle::GetInterfaceMethodImplementationSlot(EnregisteredTypeHandle pTypeHandle, EnregisteredTypeHandle pOwner, MethodDesc * pMD)
+MethodDesc* QCALLTYPE RuntimeTypeHandle::GetInterfaceMethodImplementation(EnregisteredTypeHandle pTypeHandle, EnregisteredTypeHandle pOwner, MethodDesc * pMD)
 {
     QCALL_CONTRACT;
 
-    INT32 slotNumber = -1;
+    MethodDesc* pResult = nullptr;
 
     BEGIN_QCALL;
 
@@ -1199,11 +1199,11 @@ INT32 QCALLTYPE RuntimeTypeHandle::GetInterfaceMethodImplementationSlot(Enregist
         //@TODO:              be done faster - just need to make a function FindDispatchDecl.
         DispatchSlot slot(typeHandle.GetMethodTable()->FindDispatchSlotForInterfaceMD(thOwnerOfMD, pMD));
     if (!slot.IsNull())
-            slotNumber = slot.GetMethodDesc()->GetSlot();
+            pResult = slot.GetMethodDesc();
 
     END_QCALL;
     
-    return slotNumber;
+    return pResult;
     }
     
 void QCALLTYPE RuntimeTypeHandle::GetDefaultConstructor(EnregisteredTypeHandle pTypeHandle, QCall::ObjectHandleOnStack retMethod)
index 8978e20..7a637e6 100644 (file)
@@ -248,7 +248,7 @@ public:
     void QCALLTYPE VerifyInterfaceIsImplemented(EnregisteredTypeHandle pTypeHandle, EnregisteredTypeHandle pIFaceHandle);
 
     static
-    INT32 QCALLTYPE GetInterfaceMethodImplementationSlot(EnregisteredTypeHandle pTypeHandle, EnregisteredTypeHandle pOwner, MethodDesc * pMD);
+    MethodDesc* QCALLTYPE GetInterfaceMethodImplementation(EnregisteredTypeHandle pTypeHandle, EnregisteredTypeHandle pOwner, MethodDesc * pMD);
 
     static FCDECL3(FC_BOOL_RET, GetFields, ReflectClassBaseObject *pType, INT32 **result, INT32 *pCount);
 
diff --git a/tests/src/reflection/DefaultInterfaceMethods/GetInterfaceMapConsumer.cs b/tests/src/reflection/DefaultInterfaceMethods/GetInterfaceMapConsumer.cs
new file mode 100644 (file)
index 0000000..ecb6373
--- /dev/null
@@ -0,0 +1,101 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Reflection;
+
+class Program
+{
+    static int Main()
+    {
+        bool failed = false;
+
+        {
+            var map = typeof(Fooer).GetInterfaceMap(typeof(IFoo<Fooer>));
+
+            int foundMatchMask = 0;
+
+            MethodInfo ifooDefaultMethod = typeof(IFoo<Fooer>).GetMethod("DefaultMethod");
+            MethodInfo ifooOtherMethod = typeof(IFoo<Fooer>).GetMethod("OtherMethod");
+            MethodInfo ibarOtherMethod = typeof(IBar<Fooer>).GetMethod("OtherMethod");
+
+            for (int i = 0; i < map.InterfaceMethods.Length; i++)
+            {
+                MethodInfo declMethod = map.InterfaceMethods[i];
+                MethodInfo implMethod = map.TargetMethods[i];
+
+                Console.Write("{0} ({1}) - {2} ({3}) - ", declMethod, declMethod.DeclaringType, implMethod, implMethod.DeclaringType);
+
+                if (declMethod.Equals(ifooDefaultMethod))
+                {
+                    foundMatchMask |= 1;
+                    CheckEqual(ref failed, implMethod, ifooDefaultMethod);
+                }
+                else if (declMethod.Equals(ifooOtherMethod))
+                {
+                    foundMatchMask |= 2;
+                    CheckEqual(ref failed, implMethod, ibarOtherMethod);
+                }
+                else
+                {
+                    Console.WriteLine("UNEXPECTED");
+                    failed = true;
+                }
+            }
+
+            if (foundMatchMask != 3)
+                return 10;
+        }
+
+        {
+            var map = typeof(Fooer).GetInterfaceMap(typeof(IFoo));
+
+            int foundMatchMask = 0;
+
+            MethodInfo ifooDefaultMethod = typeof(IFoo).GetMethod("DefaultMethod");
+            MethodInfo ifooOtherMethod = typeof(IFoo).GetMethod("OtherMethod");
+            MethodInfo ibarOtherMethod = typeof(IBar).GetMethod("OtherMethod");
+
+            for (int i = 0; i < map.InterfaceMethods.Length; i++)
+            {
+                MethodInfo declMethod = map.InterfaceMethods[i];
+                MethodInfo implMethod = map.TargetMethods[i];
+
+                Console.Write("{0} ({1}) - {2} ({3}) - ", declMethod, declMethod.DeclaringType, implMethod, implMethod.DeclaringType);
+
+                if (declMethod.Equals(ifooDefaultMethod))
+                {
+                    foundMatchMask |= 1;
+                    CheckEqual(ref failed, implMethod, ifooDefaultMethod);
+                }
+                else if (declMethod.Equals(ifooOtherMethod))
+                {
+                    foundMatchMask |= 2;
+                    CheckEqual(ref failed, implMethod, ibarOtherMethod);
+                }
+                else
+                {
+                    Console.WriteLine("UNEXPECTED");
+                    failed = true;
+                }
+            }
+
+            if (foundMatchMask != 3)
+                return 10;
+        }
+
+        return failed ? -1 : 100;
+    }
+
+    static void CheckEqual(ref bool failed, MethodInfo method1, MethodInfo method2)
+    {
+        if (method1.Equals(method2))
+            Console.WriteLine("OK");
+        else
+        {
+            Console.WriteLine("FAIL");
+            failed = true;
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/src/reflection/DefaultInterfaceMethods/GetInterfaceMapConsumer.csproj b/tests/src/reflection/DefaultInterfaceMethods/GetInterfaceMapConsumer.csproj
new file mode 100644 (file)
index 0000000..9557d00
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <CLRTestKind>BuildAndRun</CLRTestKind>
+    <CLRTestPriority>0</CLRTestPriority>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "></PropertyGroup>
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <ItemGroup>
+    <!-- Add Compile Object Here -->
+    <Compile Include="GetInterfaceMapConsumer.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="GetInterfaceMapProvider.ilproj" />
+  </ItemGroup>
+  <ItemGroup>
+    <NoWarn Include="42016,42020,42025,42024" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/tests/src/reflection/DefaultInterfaceMethods/GetInterfaceMapProvider.il b/tests/src/reflection/DefaultInterfaceMethods/GetInterfaceMapProvider.il
new file mode 100644 (file)
index 0000000..0378598
--- /dev/null
@@ -0,0 +1,81 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+.assembly extern mscorlib { }
+
+.assembly GetInterfaceMapProvider { }
+
+.class interface public abstract auto ansi IFoo
+{
+  .method public hidebysig newslot virtual instance int32 DefaultMethod(int32) cil managed
+  {
+    ldarg.1
+    ldc.i4 50
+    add
+    ret
+  }
+
+  .method public hidebysig newslot virtual abstract instance int32 OtherMethod(int32) cil managed
+  {
+  }
+
+  .method public hidebysig instance int32 InstanceMethod(int32) cil managed
+  {
+    ldnull
+    throw
+  }
+
+  .method public hidebysig static int32 StaticMethod(int32) cil managed
+  {
+    ldnull
+    throw
+  }
+}
+
+.class interface public abstract auto ansi IBar implements IFoo
+{
+  .method public hidebysig newslot virtual final instance int32 OtherMethod(int32) cil managed
+  {
+    .override IFoo::OtherMethod
+    ldarg.1
+    ret
+  }
+}
+
+.class interface public abstract auto ansi IFoo`1<T>
+{
+  .method public hidebysig newslot virtual instance valuetype [mscorlib]System.RuntimeTypeHandle DefaultMethod() cil managed
+  {
+    ldtoken !T
+    ret
+  }
+
+  .method public hidebysig newslot virtual abstract instance int32 OtherMethod(int32) cil managed
+  {
+  }
+}
+
+.class interface public abstract auto ansi IBar`1<T> implements class IFoo`1<!T>
+{
+  .method public hidebysig newslot virtual final instance int32 OtherMethod(int32) cil managed
+  {
+    .override class IFoo`1<!T>::OtherMethod
+    ldarg.1
+    ret
+  }
+
+}
+
+.class public auto ansi beforefieldinit Fooer
+       extends [mscorlib]System.Object
+       implements IBar, class IBar`1<class Fooer>
+{
+  .method public hidebysig specialname rtspecialname 
+          instance void .ctor() cil managed
+  {
+    ldarg.0
+    call instance void [mscorlib]System.Object::.ctor()
+    ret
+  }
+}
diff --git a/tests/src/reflection/DefaultInterfaceMethods/GetInterfaceMapProvider.ilproj b/tests/src/reflection/DefaultInterfaceMethods/GetInterfaceMapProvider.ilproj
new file mode 100644 (file)
index 0000000..513f38e
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+  </PropertyGroup>
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <PropertyGroup>
+    
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="GetInterfaceMapProvider.il" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' ">
+  </PropertyGroup> 
+</Project>
diff --git a/tests/src/reflection/DefaultInterfaceMethods/InvokeConsumer.cs b/tests/src/reflection/DefaultInterfaceMethods/InvokeConsumer.cs
new file mode 100644 (file)
index 0000000..742fd24
--- /dev/null
@@ -0,0 +1,45 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+
+class Program
+{
+    static int Main()
+    {
+        if ((int)typeof(IFoo).GetMethod("StaticMethod").Invoke(null, new object[] { 1 }) != 31)
+            return 1;
+
+        if ((int)typeof(IFoo).GetMethod("DefaultMethod").Invoke(new Fooer(), new object[] { 1 }) != 51)
+            return 2;
+
+        if ((int)typeof(IFoo).GetMethod("InstanceMethod").Invoke(new Fooer(), new object[] { 1 }) != 21)
+            return 3;
+
+        if (!((RuntimeTypeHandle)typeof(IFoo<Fooer>).GetMethod("StaticMethod").Invoke(null, new object[] { })).Equals(typeof(Fooer[,]).TypeHandle))
+            return 11;
+
+        if (!((RuntimeTypeHandle)typeof(IFoo<Fooer>).GetMethod("DefaultMethod").Invoke(new Fooer(), new object[] { })).Equals(typeof(Fooer).TypeHandle))
+            return 12;
+
+        // Likely failing due to https://github.com/dotnet/coreclr/issues/15241
+        //if (!((RuntimeTypeHandle)typeof(IFoo<Fooer>).GetMethod("InstanceMethod").Invoke(new Fooer(), new object[] { })).Equals(typeof(Fooer[]).TypeHandle))
+        //    return 13;
+
+        if ((int)typeof(IFoo).GetMethod("DefaultMethod").Invoke(new ValueFooer(), new object[] { 1 }) != 51)
+            return 22;
+
+        if ((int)typeof(IFoo).GetMethod("InstanceMethod").Invoke(new ValueFooer(), new object[] { 1 }) != 21)
+            return 23;
+
+        if (!((RuntimeTypeHandle)typeof(IFoo<Fooer>).GetMethod("DefaultMethod").Invoke(new ValueFooer(), new object[] { })).Equals(typeof(Fooer).TypeHandle))
+            return 32;
+
+        // Likely failing due to https://github.com/dotnet/coreclr/issues/15241
+        //if (!((RuntimeTypeHandle)typeof(IFoo<Fooer>).GetMethod("InstanceMethod").Invoke(new ValueFooer(), new object[] { })).Equals(typeof(Fooer[]).TypeHandle))
+        //    return 33;
+
+        return 100;
+    }
+}
diff --git a/tests/src/reflection/DefaultInterfaceMethods/InvokeConsumer.csproj b/tests/src/reflection/DefaultInterfaceMethods/InvokeConsumer.csproj
new file mode 100644 (file)
index 0000000..d42efa5
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <CLRTestKind>BuildAndRun</CLRTestKind>
+    <CLRTestPriority>0</CLRTestPriority>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "></PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "></PropertyGroup>
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <ItemGroup>
+    <!-- Add Compile Object Here -->
+    <Compile Include="InvokeConsumer.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="InvokeProvider.ilproj" />
+  </ItemGroup>
+  <ItemGroup>
+    <NoWarn Include="42016,42020,42025,42024" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/tests/src/reflection/DefaultInterfaceMethods/InvokeProvider.il b/tests/src/reflection/DefaultInterfaceMethods/InvokeProvider.il
new file mode 100644 (file)
index 0000000..d1f6e73
--- /dev/null
@@ -0,0 +1,74 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+.assembly extern mscorlib { }
+
+.assembly InvokeProvider { }
+
+.class interface public abstract auto ansi IFoo
+{
+  .method public hidebysig newslot virtual instance int32 DefaultMethod(int32) cil managed
+  {
+    ldarg.1
+    ldc.i4 50
+    add
+    ret
+  }
+
+  .method public hidebysig instance int32 InstanceMethod(int32) cil managed
+  {
+    ldarg.1
+    ldc.i4 20
+    add
+    ret
+  }
+
+  .method public hidebysig static int32 StaticMethod(int32) cil managed
+  {
+    ldarg.0
+    ldc.i4 30
+    add
+    ret
+  }
+}
+
+.class interface public abstract auto ansi IFoo`1<T>
+{
+  .method public hidebysig newslot virtual instance valuetype [mscorlib]System.RuntimeTypeHandle DefaultMethod() cil managed
+  {
+    ldtoken !T
+    ret
+  }
+
+  .method public hidebysig instance valuetype [mscorlib]System.RuntimeTypeHandle InstanceMethod() cil managed
+  {
+    ldtoken !T[]
+    ret
+  }
+
+  .method public hidebysig static valuetype [mscorlib]System.RuntimeTypeHandle StaticMethod() cil managed
+  {
+    ldtoken !T[,]
+    ret
+  }
+}
+
+.class public auto ansi beforefieldinit Fooer
+       extends [mscorlib]System.Object
+       implements IFoo, class IFoo`1<class Fooer>
+{
+  .method public hidebysig specialname rtspecialname 
+          instance void .ctor() cil managed
+  {
+    ldarg.0
+    call instance void [mscorlib]System.Object::.ctor()
+    ret
+  }
+}
+
+.class public sequential ansi sealed beforefieldinit ValueFooer
+       extends [mscorlib]System.ValueType
+       implements IFoo, class IFoo`1<class Fooer>
+{
+}
diff --git a/tests/src/reflection/DefaultInterfaceMethods/InvokeProvider.ilproj b/tests/src/reflection/DefaultInterfaceMethods/InvokeProvider.ilproj
new file mode 100644 (file)
index 0000000..21573d8
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+  </PropertyGroup>
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <PropertyGroup>
+    
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="InvokeProvider.il" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' ">
+  </PropertyGroup> 
+</Project>