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

[release/9.0-staging] Support generic fields in PersistedAssemblyBuilder #110839

Open
wants to merge 5 commits into
base: release/9.0-staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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 @@ -734,13 +734,16 @@ private EntityHandle GetMemberReferenceHandle(MemberInfo memberInfo)
{
case FieldInfo field:
Type declaringType = field.DeclaringType!;
if (field.DeclaringType!.IsGenericTypeDefinition)
if (declaringType.IsGenericTypeDefinition)
{
//The type of the field has to be fully instantiated type.
declaringType = declaringType.MakeGenericType(declaringType.GetGenericArguments());
}

Type fieldType = ((FieldInfo)GetOriginalMemberIfConstructedType(field)).FieldType;
memberHandle = AddMemberReference(field.Name, GetTypeHandle(declaringType),
MetadataSignatureHelper.GetFieldSignature(field.FieldType, field.GetRequiredCustomModifiers(), field.GetOptionalCustomModifiers(), this));
MetadataSignatureHelper.GetFieldSignature(fieldType, field.GetRequiredCustomModifiers(), field.GetOptionalCustomModifiers(), this));

break;
case ConstructorInfo ctor:
ctor = (ConstructorInfo)GetOriginalMemberIfConstructedType(ctor);
Expand Down Expand Up @@ -809,17 +812,17 @@ internal static SignatureCallingConvention GetSignatureConvention(CallingConvent
return convention;
}

private MemberInfo GetOriginalMemberIfConstructedType(MethodBase methodBase)
private MemberInfo GetOriginalMemberIfConstructedType(MemberInfo memberInfo)
{
Type declaringType = methodBase.DeclaringType!;
Type declaringType = memberInfo.DeclaringType!;
if (declaringType.IsConstructedGenericType &&
declaringType.GetGenericTypeDefinition() is not TypeBuilderImpl &&
!ContainsTypeBuilder(declaringType.GetGenericArguments()))
{
return declaringType.GetGenericTypeDefinition().GetMemberWithSameMetadataDefinitionAs(methodBase);
return declaringType.GetGenericTypeDefinition().GetMemberWithSameMetadataDefinitionAs(memberInfo);
}

return methodBase;
return memberInfo;
}

private static Type[] ParameterTypes(ParameterInfo[] parameterInfos)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using Xunit;

namespace System.Reflection.Emit.Tests
Expand Down Expand Up @@ -124,11 +126,15 @@ public void CreateMembersThatUsesTypeLoadedFromCoreAssemblyTest()
}
}

private static TypeBuilder CreateAssemblyAndDefineType(out PersistedAssemblyBuilder assemblyBuilder)
private static ModuleBuilder CreateAssembly(out PersistedAssemblyBuilder assemblyBuilder)
{
assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilder(s_assemblyName);
return assemblyBuilder.DefineDynamicModule("MyModule")
.DefineType("TestInterface", TypeAttributes.Interface | TypeAttributes.Abstract);
return assemblyBuilder.DefineDynamicModule("MyModule");
}

private static TypeBuilder CreateAssemblyAndDefineType(out PersistedAssemblyBuilder assemblyBuilder)
{
return CreateAssembly(out assemblyBuilder).DefineType("TestInterface", TypeAttributes.Interface | TypeAttributes.Abstract);
}

[Fact]
Expand Down Expand Up @@ -205,6 +211,83 @@ public void SaveGenericTypeParametersForAType(string[] typeParamNames)
}
}

private class GenericClassWithGenericField<T>
{
#pragma warning disable CS0649
public T F;
#pragma warning restore CS0649
}

private class GenericClassWithNonGenericField<T>
{
#pragma warning disable CS0649
public int F;
#pragma warning restore CS0649
}

public static IEnumerable<object[]> GenericTypesWithField()
{
yield return new object[] { typeof(GenericClassWithGenericField<int>), true };
yield return new object[] { typeof(GenericClassWithNonGenericField<bool>), false };
}

[Theory]
[MemberData(nameof(GenericTypesWithField))]
public void SaveGenericField(Type declaringType, bool shouldFieldBeGeneric)
{
using (TempFile file = TempFile.Create())
{
ModuleBuilder mb = CreateAssembly(out PersistedAssemblyBuilder assemblyBuilder);
TypeBuilder tb = mb.DefineType("C", TypeAttributes.Class);
MethodBuilder method = tb.DefineMethod("TestMethod", MethodAttributes.Public, returnType: typeof(int), parameterTypes: null);
ILGenerator il = method.GetILGenerator();
il.Emit(OpCodes.Newobj, declaringType.GetConstructor([]));
il.Emit(OpCodes.Ldfld, declaringType.GetField("F"));
il.Emit(OpCodes.Ret);
Type createdType = tb.CreateType();
assemblyBuilder.Save(file.Path);

using (FileStream stream = File.OpenRead(file.Path))
{
using (PEReader peReader = new PEReader(stream))
{
bool found = false;
MetadataReader metadataReader = peReader.GetMetadataReader();
foreach (MemberReferenceHandle memberRefHandle in metadataReader.MemberReferences)
{
MemberReference memberRef = metadataReader.GetMemberReference(memberRefHandle);
if (memberRef.GetKind() == MemberReferenceKind.Field)
{
Assert.False(found);
found = true;

Assert.Equal("F", metadataReader.GetString(memberRef.Name));

// A reference to a generic field should point to the open generic field, and not the resolved generic type.
Assert.Equal(shouldFieldBeGeneric, IsGenericField(metadataReader.GetBlobReader(memberRef.Signature)));
}
}

Assert.True(found);
}
}
}

static bool IsGenericField(BlobReader signatureReader)
{
while (signatureReader.RemainingBytes > 0)
{
SignatureTypeCode typeCode = signatureReader.ReadSignatureTypeCode();
if (typeCode == SignatureTypeCode.GenericTypeParameter)
{
return true;
}
}

return false;
}
}

private static void SetVariousGenericParameterValues(GenericTypeParameterBuilder[] typeParams)
{
typeParams[0].SetInterfaceConstraints([typeof(IAccess), typeof(INoMethod)]);
Expand Down
Loading