Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix MethodInfo Emit for generic interfaces #40587

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ private TypeBuilder GetTypeBuilder()
{
return m_methodBuilder.GetTypeBuilder();
}
internal SignatureHelper GetMethodSignature()
{
return m_methodBuilder.GetMethodSignature();
}
#endregion

#region Object Overrides
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,8 @@ public override void EndScope()
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);
Expand All @@ -442,17 +444,28 @@ private int GetMemberRefToken(MethodBase methodInfo, Type[]? optionalParameterTy
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)
Expand All @@ -465,13 +478,17 @@ internal override SignatureHelper GetMemberRefSignature(
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 (var i = 0; i < parameterTypes.Length; i++)
{
sig.AddArgument(parameterTypes[i], requiredCustomModifiers![i], optionalCustomModifiers![i]);
}
}
if (optionalParameterTypes != null && optionalParameterTypes.Length != 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,16 +162,24 @@ private int GetMethodToken(MethodBase method, Type[]? optionalParameterTypes, bo
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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,12 +357,11 @@ private static RuntimeModule GetRuntimeModuleFromModule(Module? m)
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)
{
Expand All @@ -387,55 +386,22 @@ private int GetMemberRefToken(MethodBase method, IEnumerable<Type>? optionalPara

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)
{
Expand All @@ -460,15 +426,16 @@ private int GetMemberRefToken(MethodBase method, IEnumerable<Type>? optionalPara
}

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 (var i = 0; i < parameterTypes.Length; i++)
{
sig.AddArgument(t);
sig.AddArgument(parameterTypes[i], requiredCustomModifiers![i], optionalCustomModifiers![i]);
}
}

Expand All @@ -491,6 +458,90 @@ internal SignatureHelper GetMemberRefSignature(CallingConventions call, Type? re
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);
Expand Down Expand Up @@ -1261,7 +1312,7 @@ private MethodToken GetMethodTokenNoLock(MethodInfo method, bool getGenericTypeD
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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()
{
var methodType = typeof(IWithIn<int>);
var method = methodType.GetMethod("Method");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: We use var only when the type is obvious from the right side.

https://github.com/dotnet/runtime/blob/master/docs/coding-guidelines/coding-style.md

var getMethodFromHandle = typeof(MethodBase).GetMethod("GetMethodFromHandle", new[] { typeof(RuntimeMethodHandle), typeof(RuntimeTypeHandle) });

ModuleBuilder moduleBuilder = Helpers.DynamicModule();
var typeBuilder = moduleBuilder.DefineType("DynamicType", TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Class);

var methodBuilder = typeBuilder.DefineMethod("Get", MethodAttributes.Public | MethodAttributes.Static, typeof(MethodBase), new Type[0]);
var ilBuilder = methodBuilder.GetILGenerator();
ilBuilder.Emit(OpCodes.Ldtoken, method);
ilBuilder.Emit(OpCodes.Ldtoken, methodType);
ilBuilder.Emit(OpCodes.Call, getMethodFromHandle);
ilBuilder.Emit(OpCodes.Ret);

var type = typeBuilder.CreateType();

var genMethod = type.GetMethod("Get");
var il = genMethod.GetMethodBody().GetILAsByteArray();

var ilMethodMetadataToken = BitConverter.ToInt32(il, 1);
var resolvedMethod = type.Module.ResolveMethod(ilMethodMetadataToken);
Assert.Equal(method, resolvedMethod);
var methodBase = (MethodBase)genMethod.Invoke(null, null);
Assert.Equal(method, methodBase);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand Down