Skip to content

Commit

Permalink
Fixed Emit MethodInfo for interface generic
Browse files Browse the repository at this point in the history
Signature generation now includes CustomModifiers
MethodBuilder and ConstructorBuilder passes their SignatureHelper for
generation

Fixes dotnet#25958
  • Loading branch information
wzchua committed Aug 9, 2020
1 parent aa5fdab commit 253b998
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 56 deletions.
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);
}

if (optionalParameterTypes is {} && optionalParameterTypes.Length > 0)
{
sigHelp.AddSentinel();
sigHelp.AddArguments(optionalParameterTypes, null, null);
}

byte[] sigBytes = GetMemberRefSignature(method.CallingConvention, returnType, parameterTypes,
optionalParameterTypes, cGenericParameters).InternalGetSignature(out int sigLength);
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,89 @@ 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)
{
while (true)
{
switch (method)
{
case MethodBuilder methodBuilder:
return methodBuilder.GetMethodSignature();
case ConstructorBuilder constructorBuilder:
return constructorBuilder.GetMethodSignature();
case MethodOnTypeBuilderInstantiation motbi:
method = motbi.m_method;
continue;
case ConstructorOnTypeBuilderInstantiation cotbi:
method = cotbi.m_ctor;
continue;
}

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 +1311,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,60 @@
// 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 class ILGeneratorEmitMethodInfo
{
public interface IWithIn<T>
{
void Method(in RuntimeMethodHandle arg);
}

public sealed class WithIn : IWithIn<int>
{
public void Method(in RuntimeMethodHandle arg)
{

}
}

[Fact]
public void EmitMethodInfo()
{
var testInstance = new WithIn();
var methodType = typeof(IWithIn<int>);
var method = methodType.GetMethod("Method");

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

var methodBuilder = typeBuilder.DefineMethod("Call", MethodAttributes.Public | MethodAttributes.Static, null, new Type[] { typeof(IWithIn<int>) });

var ilBuilder = methodBuilder.GetILGenerator();
ilBuilder.Emit(OpCodes.Ldarg_0);
ilBuilder.Emit(OpCodes.Ldtoken, method);
ilBuilder.Emit(OpCodes.Callvirt, method);
ilBuilder.Emit(OpCodes.Ret);

var type = typeBuilder.CreateType();
var genMethod = type.GetMethod("Call");
genMethod.Invoke(null, new object[] { testInstance });

var il = genMethod.GetMethodBody().GetILAsByteArray();

var ilMethodMetadataToken = BitConverter.ToInt32(il, 2);
var resolvedMethod = type.Module.ResolveMethod(ilMethodMetadataToken);
Assert.Equal(method, resolvedMethod);
ilMethodMetadataToken = BitConverter.ToInt32(il, 7);
resolvedMethod = type.Module.ResolveMethod(ilMethodMetadataToken);
Assert.Equal(method, resolvedMethod);
}
}
}
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

0 comments on commit 253b998

Please sign in to comment.