Make it possible to Reflection.Emit default interface methods (#16257)
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>
Thu, 8 Feb 2018 06:54:39 +0000 (07:54 +0100)
committerJan Kotas <jkotas@microsoft.com>
Thu, 8 Feb 2018 06:54:39 +0000 (22:54 -0800)
Fixes #15648.

src/mscorlib/src/System/Reflection/Emit/MethodBuilder.cs
src/mscorlib/src/System/Reflection/Emit/TypeBuilder.cs
tests/src/reflection/DefaultInterfaceMethods/Emit.cs [new file with mode: 0644]
tests/src/reflection/DefaultInterfaceMethods/Emit.csproj [new file with mode: 0644]

index daa6d76..f6d73d9 100644 (file)
@@ -125,6 +125,7 @@ namespace System.Reflection.Emit
                 throw new ArgumentException(SR.Arg_NoStaticVirtual);
             }
 
+#if !FEATURE_DEFAULT_INTERFACES
             if ((attributes & MethodAttributes.SpecialName) != MethodAttributes.SpecialName)
             {
                 if ((type.Attributes & TypeAttributes.Interface) == TypeAttributes.Interface)
@@ -136,6 +137,7 @@ namespace System.Reflection.Emit
                         throw new ArgumentException(SR.Argument_BadAttributeOnInterfaceMethod);
                 }
             }
+#endif
 
             m_callingConvention = callingConvention;
 
index ef971a5..961f5d3 100644 (file)
@@ -1384,12 +1384,14 @@ namespace System.Reflection.Emit
 
             ThrowIfCreated();
 
+#if !FEATURE_DEFAULT_INTERFACES
             if (!m_isHiddenGlobalType)
             {
                 if (((m_iAttr & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Interface) &&
                    (attributes & MethodAttributes.Abstract) == 0 && (attributes & MethodAttributes.Static) == 0)
                     throw new ArgumentException(SR.Argument_BadAttributeOnInterfaceMethod);
             }
+#endif
 
             // pass in Method attributes
             MethodBuilder method = new MethodBuilder(
diff --git a/tests/src/reflection/DefaultInterfaceMethods/Emit.cs b/tests/src/reflection/DefaultInterfaceMethods/Emit.cs
new file mode 100644 (file)
index 0000000..e57f7c1
--- /dev/null
@@ -0,0 +1,86 @@
+// 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.Collections.Generic;
+using System.Reflection;
+using System.Reflection.Emit;
+
+class Program
+{
+    static int Main()
+    {
+        var ab = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("Mine"), AssemblyBuilderAccess.Run);
+        var modb = ab.DefineDynamicModule("Mine.dll");
+
+        //
+        // Set up the IFoo interface
+        //
+
+        var ifooType = modb.DefineType("IFoo", TypeAttributes.Interface | TypeAttributes.Abstract | TypeAttributes.Public);
+
+        // Define a simple instance method on the interface
+        {
+            var mb = ifooType.DefineMethod("InstanceMethod", MethodAttributes.Public, typeof(int), Type.EmptyTypes);
+            var ilg = mb.GetILGenerator();
+            ilg.Emit(OpCodes.Ldc_I4_1);
+            ilg.Emit(OpCodes.Ret);
+        }
+
+        // Define a default interface method
+        {
+            var mb = ifooType.DefineMethod("DefaultMethod", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.NewSlot, typeof(int), Type.EmptyTypes);
+            var ilg = mb.GetILGenerator();
+            ilg.Emit(OpCodes.Ldc_I4_2);
+            ilg.Emit(OpCodes.Ret);
+        }
+
+        // Define a regular interface method
+        {
+            var mb = ifooType.DefineMethod("InterfaceMethod", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Abstract, typeof(int), Type.EmptyTypes);
+        }
+
+        ifooType.CreateTypeInfo();
+
+        //
+        // Set up the IBar interface
+        //
+
+        var ibarType = modb.DefineType("IBar", TypeAttributes.Interface | TypeAttributes.Abstract | TypeAttributes.Public, null, new Type[] { ifooType });
+
+        // Override the regular interface method on IFoo with a default implementation
+        {
+            var mb = ibarType.DefineMethod("InterfaceMethodImpl", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.NewSlot | MethodAttributes.Final, typeof(int), Type.EmptyTypes);
+            var ilg = mb.GetILGenerator();
+            ilg.Emit(OpCodes.Ldc_I4_4);
+            ilg.Emit(OpCodes.Ret);
+
+            ibarType.DefineMethodOverride(mb, ifooType.GetMethod("InterfaceMethod"));
+        }
+
+        ibarType.CreateTypeInfo();
+
+        //
+        // Make a simple Foo class that implements IBar
+        //
+
+        var fooType = modb.DefineType("Foo", TypeAttributes.Class | TypeAttributes.Public, typeof(object), new Type[] { ibarType });
+
+        fooType.CreateTypeInfo();
+
+        //
+        // Test what we created
+        //
+
+        object o = Activator.CreateInstance(fooType);
+
+        int result = 0;
+
+        result |= (int)ifooType.GetMethod("InstanceMethod").Invoke(o, null);
+        result |= (int)ifooType.GetMethod("DefaultMethod").Invoke(o, null);
+        result |= (int)ifooType.GetMethod("InterfaceMethod").Invoke(o, null);
+
+        return result == 0x07 ? 100 : result;
+    }
+}
diff --git a/tests/src/reflection/DefaultInterfaceMethods/Emit.csproj b/tests/src/reflection/DefaultInterfaceMethods/Emit.csproj
new file mode 100644 (file)
index 0000000..ab272ad
--- /dev/null
@@ -0,0 +1,35 @@
+<?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="Emit.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </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