Fix MethodInfo Emit for generic interfaces (#40587)
authorWei Zheng <13881045+wzchua@users.noreply.github.com>
Mon, 24 Aug 2020 15:25:17 +0000 (23:25 +0800)
committerGitHub <noreply@github.com>
Mon, 24 Aug 2020 15:25:17 +0000 (10:25 -0500)
src/coreclr/src/System.Private.CoreLib/src/System/Reflection/Emit/ConstructorBuilder.cs
src/coreclr/src/System.Private.CoreLib/src/System/Reflection/Emit/DynamicILGenerator.cs
src/coreclr/src/System.Private.CoreLib/src/System/Reflection/Emit/ILGenerator.cs
src/coreclr/src/System.Private.CoreLib/src/System/Reflection/Emit/ModuleBuilder.cs
src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/EmitMethodInfo.cs [new file with mode: 0644]
src/libraries/System.Reflection.Emit.ILGeneration/tests/System.Reflection.Emit.ILGeneration.Tests.csproj

index de668d5..f840524 100644 (file)
@@ -45,6 +45,10 @@ namespace System.Reflection.Emit
         {
             return m_methodBuilder.GetTypeBuilder();
         }
+        internal SignatureHelper GetMethodSignature()
+        {
+            return m_methodBuilder.GetMethodSignature();
+        }
         #endregion
 
         #region Object Overrides
index 34d501c..693a293 100644 (file)
@@ -428,6 +428,8 @@ namespace System.Reflection.Emit
         private int GetMemberRefToken(MethodBase methodInfo, Type[]? optionalParameterTypes)
         {
             Type[]? parameterTypes;
+            Type[][]? requiredCustomModifiers;
+            Type[][]? optionalCustomModifiers;
 
             if (optionalParameterTypes != null && (methodInfo.CallingConvention & CallingConventions.VarArgs) == 0)
                 throw new InvalidOperationException(SR.InvalidOperation_NotAVarArgCallingConvention);
@@ -442,17 +444,28 @@ namespace System.Reflection.Emit
             if (paramInfo != null && paramInfo.Length != 0)
             {
                 parameterTypes = new Type[paramInfo.Length];
+                requiredCustomModifiers = new Type[parameterTypes.Length][];
+                optionalCustomModifiers = new Type[parameterTypes.Length][];
+
                 for (int i = 0; i < paramInfo.Length; i++)
+                {
                     parameterTypes[i] = paramInfo[i].ParameterType;
+                    requiredCustomModifiers[i] = paramInfo[i].GetRequiredCustomModifiers();
+                    optionalCustomModifiers[i] = paramInfo[i].GetOptionalCustomModifiers();
+                }
             }
             else
             {
                 parameterTypes = null;
+                requiredCustomModifiers = null;
+                optionalCustomModifiers = null;
             }
 
             SignatureHelper sig = GetMemberRefSignature(methodInfo.CallingConvention,
                                                      MethodBuilder.GetMethodBaseReturnType(methodInfo),
                                                      parameterTypes,
+                                                     requiredCustomModifiers,
+                                                     optionalCustomModifiers,
                                                      optionalParameterTypes);
 
             if (rtMeth != null)
@@ -465,13 +478,17 @@ namespace System.Reflection.Emit
                                                 CallingConventions call,
                                                 Type? returnType,
                                                 Type[]? parameterTypes,
+                                                Type[][]? requiredCustomModifiers,
+                                                Type[][]? optionalCustomModifiers,
                                                 Type[]? optionalParameterTypes)
         {
             SignatureHelper sig = SignatureHelper.GetMethodSigHelper(call, returnType);
             if (parameterTypes != null)
             {
-                foreach (Type t in parameterTypes)
-                    sig.AddArgument(t);
+                for (int i = 0; i < parameterTypes.Length; i++)
+                {
+                    sig.AddArgument(parameterTypes[i], requiredCustomModifiers![i], optionalCustomModifiers![i]);
+                }
             }
             if (optionalParameterTypes != null && optionalParameterTypes.Length != 0)
             {
index c587b18..43ecc3a 100644 (file)
@@ -162,16 +162,24 @@ namespace System.Reflection.Emit
             return ((ModuleBuilder)m_methodBuilder.Module).GetMethodTokenInternal(method, optionalParameterTypes, useMethodDef);
         }
 
+        internal SignatureHelper GetMemberRefSignature(
+            CallingConventions call,
+            Type? returnType,
+            Type[]? parameterTypes,
+            Type[]? optionalParameterTypes)
+        {
+            return GetMemberRefSignature(call, returnType, parameterTypes, null, null, optionalParameterTypes);
+        }
         internal virtual SignatureHelper GetMemberRefSignature(CallingConventions call, Type? returnType,
-            Type[]? parameterTypes, Type[]? optionalParameterTypes)
+            Type[]? parameterTypes, Type[][]? requiredCustomModifiers, Type[][]? optionalCustomModifiers, Type[]? optionalParameterTypes)
         {
-            return GetMemberRefSignature(call, returnType, parameterTypes, optionalParameterTypes, 0);
+            return GetMemberRefSignature(call, returnType, parameterTypes, requiredCustomModifiers, optionalCustomModifiers, optionalParameterTypes, 0);
         }
 
         private SignatureHelper GetMemberRefSignature(CallingConventions call, Type? returnType,
-            Type[]? parameterTypes, Type[]? optionalParameterTypes, int cGenericParameters)
+            Type[]? parameterTypes, Type[][]? requiredCustomModifiers, Type[][]? optionalCustomModifiers, Type[]? optionalParameterTypes, int cGenericParameters)
         {
-            return ((ModuleBuilder)m_methodBuilder.Module).GetMemberRefSignature(call, returnType, parameterTypes, optionalParameterTypes, cGenericParameters);
+            return ((ModuleBuilder)m_methodBuilder.Module).GetMemberRefSignature(call, returnType, parameterTypes, requiredCustomModifiers, optionalCustomModifiers, optionalParameterTypes, cGenericParameters);
         }
 
         internal byte[]? BakeByteArray()
index aec1ca3..c390d48 100644 (file)
@@ -357,12 +357,11 @@ namespace System.Reflection.Emit
             return (m as RuntimeModule)!;
         }
 
-        private int GetMemberRefToken(MethodBase method, IEnumerable<Type>? optionalParameterTypes)
+        private int GetMemberRefToken(MethodBase method, Type[]? optionalParameterTypes)
         {
-            Type[] parameterTypes;
-            Type? returnType;
             int tkParent;
             int cGenericParameters = 0;
+            SignatureHelper sigHelp;
 
             if (method.IsGenericMethod)
             {
@@ -387,55 +386,22 @@ namespace System.Reflection.Emit
 
             if (method.DeclaringType!.IsGenericType)
             {
-                MethodBase methDef; // methodInfo = G<Foo>.M<Bar> ==> methDef = G<T>.M<S>
+                MethodBase methDef = GetGenericMethodBaseDefinition(method);
 
-                if (method is MethodOnTypeBuilderInstantiation motbi)
-                {
-                    methDef = motbi.m_method;
-                }
-                else if (method is ConstructorOnTypeBuilderInstantiation cotbi)
-                {
-                    methDef = cotbi.m_ctor;
-                }
-                else if (method is MethodBuilder || method is ConstructorBuilder)
-                {
-                    // methodInfo must be GenericMethodDefinition; trying to emit G<?>.M<S>
-                    methDef = method;
-                }
-                else
-                {
-                    Debug.Assert(method is RuntimeMethodInfo || method is RuntimeConstructorInfo);
-
-                    if (method.IsGenericMethod)
-                    {
-                        Debug.Assert(masmi != null);
-
-                        methDef = masmi.GetGenericMethodDefinition()!;
-                        methDef = methDef.Module.ResolveMethod(
-                            method.MetadataToken,
-                            methDef.DeclaringType?.GetGenericArguments(),
-                            methDef.GetGenericArguments())!;
-                    }
-                    else
-                    {
-                        methDef = method.Module.ResolveMethod(
-                            method.MetadataToken,
-                            method.DeclaringType?.GetGenericArguments(),
-                            null)!;
-                    }
-                }
-
-                parameterTypes = methDef.GetParameterTypes();
-                returnType = MethodBuilder.GetMethodBaseReturnType(methDef);
+                sigHelp = GetMemberRefSignature(methDef, cGenericParameters);
             }
             else
             {
-                parameterTypes = method.GetParameterTypes();
-                returnType = MethodBuilder.GetMethodBaseReturnType(method);
+                sigHelp = GetMemberRefSignature(method, cGenericParameters);
             }
 
-            byte[] sigBytes = GetMemberRefSignature(method.CallingConvention, returnType, parameterTypes,
-                optionalParameterTypes, cGenericParameters).InternalGetSignature(out int sigLength);
+            if (optionalParameterTypes?.Length > 0)
+            {
+                sigHelp.AddSentinel();
+                sigHelp.AddArguments(optionalParameterTypes, null, null);
+            }
+
+            byte[] sigBytes = sigHelp.InternalGetSignature(out int sigLength);
 
             if (method.DeclaringType!.IsGenericType)
             {
@@ -460,15 +426,16 @@ namespace System.Reflection.Emit
         }
 
         internal SignatureHelper GetMemberRefSignature(CallingConventions call, Type? returnType,
-            Type[]? parameterTypes, IEnumerable<Type>? optionalParameterTypes, int cGenericParameters)
+            Type[]? parameterTypes, Type[][]? requiredCustomModifiers, Type[][]? optionalCustomModifiers,
+            IEnumerable<Type>? optionalParameterTypes, int cGenericParameters)
         {
             SignatureHelper sig = SignatureHelper.GetMethodSigHelper(this, call, returnType, cGenericParameters);
 
             if (parameterTypes != null)
             {
-                foreach (Type t in parameterTypes)
+                for (int i = 0; i < parameterTypes.Length; i++)
                 {
-                    sig.AddArgument(t);
+                    sig.AddArgument(parameterTypes[i], requiredCustomModifiers![i], optionalCustomModifiers![i]);
                 }
             }
 
@@ -491,6 +458,90 @@ namespace System.Reflection.Emit
             return sig;
         }
 
+        private MethodBase GetGenericMethodBaseDefinition(MethodBase methodBase)
+        {
+            // methodInfo = G<Foo>.M<Bar> ==> methDef = G<T>.M<S>
+            MethodInfo? masmi = methodBase as MethodInfo;
+            MethodBase methDef;
+
+            if (methodBase is MethodOnTypeBuilderInstantiation motbi)
+            {
+                methDef = motbi.m_method;
+            }
+            else if (methodBase is ConstructorOnTypeBuilderInstantiation cotbi)
+            {
+                methDef = cotbi.m_ctor;
+            }
+            else if (methodBase is MethodBuilder || methodBase is ConstructorBuilder)
+            {
+                // methodInfo must be GenericMethodDefinition; trying to emit G<?>.M<S>
+                methDef = methodBase;
+            }
+            else
+            {
+                Debug.Assert(methodBase is RuntimeMethodInfo || methodBase is RuntimeConstructorInfo);
+
+                if (methodBase.IsGenericMethod)
+                {
+                    Debug.Assert(masmi != null);
+
+                    methDef = masmi.GetGenericMethodDefinition()!;
+                    methDef = methDef.Module.ResolveMethod(
+                        methodBase.MetadataToken,
+                        methDef.DeclaringType?.GetGenericArguments(),
+                        methDef.GetGenericArguments())!;
+                }
+                else
+                {
+                    methDef = methodBase.Module.ResolveMethod(
+                        methodBase.MetadataToken,
+                        methodBase.DeclaringType?.GetGenericArguments(),
+                        null)!;
+                }
+            }
+
+            return methDef;
+        }
+
+        internal SignatureHelper GetMemberRefSignature(MethodBase? method, int cGenericParameters)
+        {
+            switch (method)
+            {
+                case MethodBuilder methodBuilder:
+                    return methodBuilder.GetMethodSignature();
+                case ConstructorBuilder constructorBuilder:
+                    return constructorBuilder.GetMethodSignature();
+                case MethodOnTypeBuilderInstantiation motbi when motbi.m_method is MethodBuilder methodBuilder:
+                    return methodBuilder.GetMethodSignature();
+                case MethodOnTypeBuilderInstantiation motbi:
+                    method = motbi.m_method;
+                    break;
+                case ConstructorOnTypeBuilderInstantiation cotbi when cotbi.m_ctor is ConstructorBuilder constructorBuilder:
+                    return constructorBuilder.GetMethodSignature();
+                case ConstructorOnTypeBuilderInstantiation cotbi:
+                    method = cotbi.m_ctor;
+                    break;
+            }
+
+            Debug.Assert(method is RuntimeMethodInfo || method is RuntimeConstructorInfo);
+            ParameterInfo[] parameters = method.GetParametersNoCopy();
+
+            Type[] parameterTypes = new Type[parameters.Length];
+            Type[][] requiredCustomModifiers = new Type[parameterTypes.Length][];
+            Type[][] optionalCustomModifiers = new Type[parameterTypes.Length][];
+
+            for (int i = 0; i < parameters.Length; i++)
+            {
+                parameterTypes[i] = parameters[i].ParameterType;
+                requiredCustomModifiers[i] = parameters[i].GetRequiredCustomModifiers();
+                optionalCustomModifiers[i] = parameters[i].GetOptionalCustomModifiers();
+            }
+
+            ParameterInfo? returnParameter = method is MethodInfo mi ? mi.ReturnParameter : null;
+            SignatureHelper sigHelp = SignatureHelper.GetMethodSigHelper(this, method.CallingConvention, cGenericParameters, returnParameter?.ParameterType, returnParameter?.GetRequiredCustomModifiers(), returnParameter?.GetOptionalCustomModifiers(), parameterTypes, requiredCustomModifiers, optionalCustomModifiers);
+            return sigHelp;
+        }
+
         #endregion
 
         public override bool Equals(object? obj) => InternalModule.Equals(obj);
@@ -1261,7 +1312,7 @@ namespace System.Reflection.Emit
             return new MethodToken(mr);
         }
 
-        internal int GetMethodTokenInternal(MethodBase method, IEnumerable<Type>? optionalParameterTypes, bool useMethodDef)
+        internal int GetMethodTokenInternal(MethodBase method, Type[]? optionalParameterTypes, bool useMethodDef)
         {
             int tk;
             MethodInfo? methodInfo = method as MethodInfo;
diff --git a/src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/EmitMethodInfo.cs b/src/libraries/System.Reflection.Emit.ILGeneration/tests/ILGenerator/EmitMethodInfo.cs
new file mode 100644 (file)
index 0000000..438b24b
--- /dev/null
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace System.Reflection.Emit.Tests
+{
+    public interface IWithIn<T>
+    {
+        void Method(in int arg);
+    }
+    public class ILGeneratorEmitMethodInfo
+    {
+        [Fact]
+        public void EmitMethodInfo()
+        {
+            Type methodType = typeof(IWithIn<int>);
+            MethodInfo method = methodType.GetMethod("Method");
+            MethodInfo getMethodFromHandle = typeof(MethodBase).GetMethod("GetMethodFromHandle", new[] { typeof(RuntimeMethodHandle), typeof(RuntimeTypeHandle) });
+
+            ModuleBuilder moduleBuilder = Helpers.DynamicModule();
+            TypeBuilder typeBuilder = moduleBuilder.DefineType("DynamicType", TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Class);
+
+            MethodBuilder methodBuilder = typeBuilder.DefineMethod("Get", MethodAttributes.Public | MethodAttributes.Static, typeof(MethodBase), new Type[0]);
+            ILGenerator ilBuilder = methodBuilder.GetILGenerator();
+            ilBuilder.Emit(OpCodes.Ldtoken, method);
+            ilBuilder.Emit(OpCodes.Ldtoken, methodType);
+            ilBuilder.Emit(OpCodes.Call, getMethodFromHandle);
+            ilBuilder.Emit(OpCodes.Ret);
+
+            Type type = typeBuilder.CreateType();
+
+            MethodInfo genMethod = type.GetMethod("Get");
+            byte[] il = genMethod.GetMethodBody().GetILAsByteArray();
+
+            int ilMethodMetadataToken = BitConverter.ToInt32(il, 1);
+            MethodBase resolvedMethod = type.Module.ResolveMethod(ilMethodMetadataToken);
+            Assert.Equal(method, resolvedMethod);
+            var methodBase = (MethodBase)genMethod.Invoke(null, null);
+            Assert.Equal(method, methodBase);
+        }
+    }
+}
index 2947f91..a48a6be 100644 (file)
@@ -10,6 +10,7 @@
     <Compile Include="ILGenerator\Emit2Tests.cs" />
     <Compile Include="ILGenerator\Emit3Tests.cs" />
     <Compile Include="ILGenerator\Emit4Tests.cs" />
+    <Compile Include="ILGenerator\EmitMethodInfo.cs" />
     <Compile Include="ILGenerator\EmitWriteLineTests.cs" />
     <Compile Include="ILGenerator\ExceptionEmitTests.cs" />
     <Compile Include="ILGenerator\ILOffsetTests.cs" />