Skip to content

Commit 6a86517

Browse files
[release/9.0] Support generic fields in PersistedAssemblyBuilder (#111467)
* Support generic fields in PersistedAssemblyBuilder * Add the pragmas for unused fields * Add the pragmas for unused fields 2 * Fix test for the non-generic case * Re-use existing GetOriginalMemberIfConstructedType() --------- Co-authored-by: Steve Harter <steveharter@users.noreply.github.com>
1 parent 63cb882 commit 6a86517

File tree

2 files changed

+95
-9
lines changed

2 files changed

+95
-9
lines changed

src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs

+9-6
Original file line numberDiff line numberDiff line change
@@ -734,13 +734,16 @@ private EntityHandle GetMemberReferenceHandle(MemberInfo memberInfo)
734734
{
735735
case FieldInfo field:
736736
Type declaringType = field.DeclaringType!;
737-
if (field.DeclaringType!.IsGenericTypeDefinition)
737+
if (declaringType.IsGenericTypeDefinition)
738738
{
739739
//The type of the field has to be fully instantiated type.
740740
declaringType = declaringType.MakeGenericType(declaringType.GetGenericArguments());
741741
}
742+
743+
Type fieldType = ((FieldInfo)GetOriginalMemberIfConstructedType(field)).FieldType;
742744
memberHandle = AddMemberReference(field.Name, GetTypeHandle(declaringType),
743-
MetadataSignatureHelper.GetFieldSignature(field.FieldType, field.GetRequiredCustomModifiers(), field.GetOptionalCustomModifiers(), this));
745+
MetadataSignatureHelper.GetFieldSignature(fieldType, field.GetRequiredCustomModifiers(), field.GetOptionalCustomModifiers(), this));
746+
744747
break;
745748
case ConstructorInfo ctor:
746749
ctor = (ConstructorInfo)GetOriginalMemberIfConstructedType(ctor);
@@ -809,17 +812,17 @@ internal static SignatureCallingConvention GetSignatureConvention(CallingConvent
809812
return convention;
810813
}
811814

812-
private MemberInfo GetOriginalMemberIfConstructedType(MethodBase methodBase)
815+
private MemberInfo GetOriginalMemberIfConstructedType(MemberInfo memberInfo)
813816
{
814-
Type declaringType = methodBase.DeclaringType!;
817+
Type declaringType = memberInfo.DeclaringType!;
815818
if (declaringType.IsConstructedGenericType &&
816819
declaringType.GetGenericTypeDefinition() is not TypeBuilderImpl &&
817820
!ContainsTypeBuilder(declaringType.GetGenericArguments()))
818821
{
819-
return declaringType.GetGenericTypeDefinition().GetMemberWithSameMetadataDefinitionAs(methodBase);
822+
return declaringType.GetGenericTypeDefinition().GetMemberWithSameMetadataDefinitionAs(memberInfo);
820823
}
821824

822-
return methodBase;
825+
return memberInfo;
823826
}
824827

825828
private static Type[] ParameterTypes(ParameterInfo[] parameterInfos)

src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs

+86-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
using System.Diagnostics.CodeAnalysis;
66
using System.IO;
77
using System.Linq;
8+
using System.Reflection.Metadata;
9+
using System.Reflection.PortableExecutable;
810
using Xunit;
911

1012
namespace System.Reflection.Emit.Tests
@@ -124,11 +126,15 @@ public void CreateMembersThatUsesTypeLoadedFromCoreAssemblyTest()
124126
}
125127
}
126128

127-
private static TypeBuilder CreateAssemblyAndDefineType(out PersistedAssemblyBuilder assemblyBuilder)
129+
private static ModuleBuilder CreateAssembly(out PersistedAssemblyBuilder assemblyBuilder)
128130
{
129131
assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilder(s_assemblyName);
130-
return assemblyBuilder.DefineDynamicModule("MyModule")
131-
.DefineType("TestInterface", TypeAttributes.Interface | TypeAttributes.Abstract);
132+
return assemblyBuilder.DefineDynamicModule("MyModule");
133+
}
134+
135+
private static TypeBuilder CreateAssemblyAndDefineType(out PersistedAssemblyBuilder assemblyBuilder)
136+
{
137+
return CreateAssembly(out assemblyBuilder).DefineType("TestInterface", TypeAttributes.Interface | TypeAttributes.Abstract);
132138
}
133139

134140
[Fact]
@@ -205,6 +211,83 @@ public void SaveGenericTypeParametersForAType(string[] typeParamNames)
205211
}
206212
}
207213

214+
private class GenericClassWithGenericField<T>
215+
{
216+
#pragma warning disable CS0649
217+
public T F;
218+
#pragma warning restore CS0649
219+
}
220+
221+
private class GenericClassWithNonGenericField<T>
222+
{
223+
#pragma warning disable CS0649
224+
public int F;
225+
#pragma warning restore CS0649
226+
}
227+
228+
public static IEnumerable<object[]> GenericTypesWithField()
229+
{
230+
yield return new object[] { typeof(GenericClassWithGenericField<int>), true };
231+
yield return new object[] { typeof(GenericClassWithNonGenericField<bool>), false };
232+
}
233+
234+
[Theory]
235+
[MemberData(nameof(GenericTypesWithField))]
236+
public void SaveGenericField(Type declaringType, bool shouldFieldBeGeneric)
237+
{
238+
using (TempFile file = TempFile.Create())
239+
{
240+
ModuleBuilder mb = CreateAssembly(out PersistedAssemblyBuilder assemblyBuilder);
241+
TypeBuilder tb = mb.DefineType("C", TypeAttributes.Class);
242+
MethodBuilder method = tb.DefineMethod("TestMethod", MethodAttributes.Public, returnType: typeof(int), parameterTypes: null);
243+
ILGenerator il = method.GetILGenerator();
244+
il.Emit(OpCodes.Newobj, declaringType.GetConstructor([]));
245+
il.Emit(OpCodes.Ldfld, declaringType.GetField("F"));
246+
il.Emit(OpCodes.Ret);
247+
Type createdType = tb.CreateType();
248+
assemblyBuilder.Save(file.Path);
249+
250+
using (FileStream stream = File.OpenRead(file.Path))
251+
{
252+
using (PEReader peReader = new PEReader(stream))
253+
{
254+
bool found = false;
255+
MetadataReader metadataReader = peReader.GetMetadataReader();
256+
foreach (MemberReferenceHandle memberRefHandle in metadataReader.MemberReferences)
257+
{
258+
MemberReference memberRef = metadataReader.GetMemberReference(memberRefHandle);
259+
if (memberRef.GetKind() == MemberReferenceKind.Field)
260+
{
261+
Assert.False(found);
262+
found = true;
263+
264+
Assert.Equal("F", metadataReader.GetString(memberRef.Name));
265+
266+
// A reference to a generic field should point to the open generic field, and not the resolved generic type.
267+
Assert.Equal(shouldFieldBeGeneric, IsGenericField(metadataReader.GetBlobReader(memberRef.Signature)));
268+
}
269+
}
270+
271+
Assert.True(found);
272+
}
273+
}
274+
}
275+
276+
static bool IsGenericField(BlobReader signatureReader)
277+
{
278+
while (signatureReader.RemainingBytes > 0)
279+
{
280+
SignatureTypeCode typeCode = signatureReader.ReadSignatureTypeCode();
281+
if (typeCode == SignatureTypeCode.GenericTypeParameter)
282+
{
283+
return true;
284+
}
285+
}
286+
287+
return false;
288+
}
289+
}
290+
208291
private static void SetVariousGenericParameterValues(GenericTypeParameterBuilder[] typeParams)
209292
{
210293
typeParams[0].SetInterfaceConstraints([typeof(IAccess), typeof(INoMethod)]);

0 commit comments

Comments
 (0)