|
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