|  | 
| 5 | 5 | using System.Diagnostics.CodeAnalysis; | 
| 6 | 6 | using System.IO; | 
| 7 | 7 | using System.Linq; | 
|  | 8 | +using System.Reflection.Metadata; | 
|  | 9 | +using System.Reflection.PortableExecutable; | 
| 8 | 10 | using Xunit; | 
| 9 | 11 | 
 | 
| 10 | 12 | namespace System.Reflection.Emit.Tests | 
| @@ -124,11 +126,15 @@ public void CreateMembersThatUsesTypeLoadedFromCoreAssemblyTest() | 
| 124 | 126 |             } | 
| 125 | 127 |         } | 
| 126 | 128 | 
 | 
| 127 |  | -        private static TypeBuilder CreateAssemblyAndDefineType(out PersistedAssemblyBuilder assemblyBuilder) | 
|  | 129 | +        private static ModuleBuilder CreateAssembly(out PersistedAssemblyBuilder assemblyBuilder) | 
| 128 | 130 |         { | 
| 129 | 131 |             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); | 
| 132 | 138 |         } | 
| 133 | 139 | 
 | 
| 134 | 140 |         [Fact] | 
| @@ -205,6 +211,83 @@ public void SaveGenericTypeParametersForAType(string[] typeParamNames) | 
| 205 | 211 |             } | 
| 206 | 212 |         } | 
| 207 | 213 | 
 | 
|  | 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 | + | 
| 208 | 291 |         private static void SetVariousGenericParameterValues(GenericTypeParameterBuilder[] typeParams) | 
| 209 | 292 |         { | 
| 210 | 293 |             typeParams[0].SetInterfaceConstraints([typeof(IAccess), typeof(INoMethod)]); | 
|  | 
0 commit comments