From 36fef72a01727b0c58b8d5aa613f1711e934619e Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Thu, 19 Dec 2024 08:59:52 -0600 Subject: [PATCH] Support generic fields in PersistedAssemblyBuilder (#110372) --- .../Reflection/Emit/ModuleBuilderImpl.cs | 15 ++-- .../AssemblySaveTypeBuilderTests.cs | 89 ++++++++++++++++++- 2 files changed, 95 insertions(+), 9 deletions(-) diff --git a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs index e773ab500959f..02663bfa182d8 100644 --- a/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs +++ b/src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/ModuleBuilderImpl.cs @@ -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); @@ -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) diff --git a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs index 31536bc266c0a..f508da07364f2 100644 --- a/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs +++ b/src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/AssemblySaveTypeBuilderTests.cs @@ -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 @@ -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] @@ -205,6 +211,83 @@ public void SaveGenericTypeParametersForAType(string[] typeParamNames) } } + private class GenericClassWithGenericField + { +#pragma warning disable CS0649 + public T F; +#pragma warning restore CS0649 + } + + private class GenericClassWithNonGenericField + { +#pragma warning disable CS0649 + public int F; +#pragma warning restore CS0649 + } + + public static IEnumerable GenericTypesWithField() + { + yield return new object[] { typeof(GenericClassWithGenericField), true }; + yield return new object[] { typeof(GenericClassWithNonGenericField), 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)]);