Skip to content

Commit ceaeff5

Browse files
Add PersistedAssemblyBuilder tests for items 6 to 9 and remove scenario list
Co-authored-by: AaronRobinsonMSFT <30635565+AaronRobinsonMSFT@users.noreply.github.com>
1 parent 0227991 commit ceaeff5

File tree

4 files changed

+184
-45
lines changed

4 files changed

+184
-45
lines changed

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

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,61 @@ public void PersistedAssemblyBuilder_GenerateMetadataWithGenericEntryPoint()
271271
}
272272
}
273273

274+
[Fact]
275+
public void PersistedAssemblyBuilder_CollectibleAssemblyLoadContextIsolation()
276+
{
277+
byte[] assemblyImage = BuildAssemblyImageForMetadataInvariants(new AssemblyName("IsolatedLoadContextAssembly"), ["IsolatedType"]);
278+
TestAssemblyLoadContext context1 = new();
279+
TestAssemblyLoadContext context2 = new();
280+
281+
using MemoryStream stream1 = new MemoryStream(assemblyImage);
282+
using MemoryStream stream2 = new MemoryStream(assemblyImage);
283+
Assembly assembly1 = context1.LoadFromStream(stream1);
284+
Assembly assembly2 = context2.LoadFromStream(stream2);
285+
Type type1 = assembly1.GetType("IsolatedType");
286+
Type type2 = assembly2.GetType("IsolatedType");
287+
288+
Assert.NotNull(type1);
289+
Assert.NotNull(type2);
290+
Assert.Equal(type1.FullName, type2.FullName);
291+
Assert.NotSame(type1.Assembly, type2.Assembly);
292+
293+
context1.Unload();
294+
context2.Unload();
295+
}
296+
297+
[Fact]
298+
public void PersistedAssemblyBuilder_MetadataInvariantsEquivalentShapeSameOrder()
299+
{
300+
byte[] firstAssemblyImage = BuildAssemblyImageForMetadataInvariants(new AssemblyName("MetadataInvariantAssembly1"), ["TypeA", "TypeB"]);
301+
byte[] secondAssemblyImage = BuildAssemblyImageForMetadataInvariants(new AssemblyName("MetadataInvariantAssembly2"), ["TypeA", "TypeB"]);
302+
303+
MetadataInvariantData first = ReadMetadataInvariantData(firstAssemblyImage);
304+
MetadataInvariantData second = ReadMetadataInvariantData(secondAssemblyImage);
305+
306+
Assert.Equal(first.TypeDefinitionRowCount, second.TypeDefinitionRowCount);
307+
Assert.Equal(first.MethodDefinitionRowCount, second.MethodDefinitionRowCount);
308+
Assert.Equal(first.FieldDefinitionRowCount, second.FieldDefinitionRowCount);
309+
Assert.Equal(first.TypeDefinitionNamesInMetadataOrder, second.TypeDefinitionNamesInMetadataOrder);
310+
Assert.Equal(first.MethodDefinitionNamesInMetadataOrder, second.MethodDefinitionNamesInMetadataOrder);
311+
}
312+
313+
[Fact]
314+
public void PersistedAssemblyBuilder_MetadataInvariantsEquivalentShapeDifferentOrder()
315+
{
316+
byte[] firstAssemblyImage = BuildAssemblyImageForMetadataInvariants(new AssemblyName("MetadataInvariantAssemblyA"), ["TypeA", "TypeB"]);
317+
byte[] secondAssemblyImage = BuildAssemblyImageForMetadataInvariants(new AssemblyName("MetadataInvariantAssemblyB"), ["TypeB", "TypeA"]);
318+
319+
MetadataInvariantData first = ReadMetadataInvariantData(firstAssemblyImage);
320+
MetadataInvariantData second = ReadMetadataInvariantData(secondAssemblyImage);
321+
322+
Assert.Equal(first.TypeDefinitionRowCount, second.TypeDefinitionRowCount);
323+
Assert.Equal(first.MethodDefinitionRowCount, second.MethodDefinitionRowCount);
324+
Assert.Equal(first.FieldDefinitionRowCount, second.FieldDefinitionRowCount);
325+
Assert.Equal(first.TypeDefinitionNamesInMetadataOrder.OrderBy(static name => name), second.TypeDefinitionNamesInMetadataOrder.OrderBy(static name => name));
326+
Assert.Equal(first.MethodDefinitionNamesInMetadataOrder.OrderBy(static name => name), second.MethodDefinitionNamesInMetadataOrder.OrderBy(static name => name));
327+
}
328+
274329
[Fact]
275330
public void AssemblyWithDifferentTypes()
276331
{
@@ -561,6 +616,45 @@ private static PersistedAssemblyBuilder CreateSimplePersistedAssembly(AssemblyNa
561616
return ab;
562617
}
563618

619+
private static byte[] BuildAssemblyImageForMetadataInvariants(AssemblyName assemblyName, string[] typeNames)
620+
{
621+
PersistedAssemblyBuilder assemblyBuilder = AssemblySaveTools.PopulateAssemblyBuilder(assemblyName);
622+
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MyModule");
623+
624+
foreach (string typeName in typeNames)
625+
{
626+
TypeBuilder typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Class);
627+
MethodBuilder methodBuilder = typeBuilder.DefineMethod("Method", MethodAttributes.Public | MethodAttributes.Static, typeof(int), Type.EmptyTypes);
628+
ILGenerator ilGenerator = methodBuilder.GetILGenerator();
629+
ilGenerator.Emit(OpCodes.Ldc_I4_1);
630+
ilGenerator.Emit(OpCodes.Ret);
631+
typeBuilder.CreateType();
632+
}
633+
634+
using MemoryStream stream = new MemoryStream();
635+
assemblyBuilder.Save(stream);
636+
return stream.ToArray();
637+
}
638+
639+
private static MetadataInvariantData ReadMetadataInvariantData(byte[] assemblyImage)
640+
{
641+
using PEReader peReader = new PEReader(new MemoryStream(assemblyImage));
642+
MetadataReader metadataReader = peReader.GetMetadataReader();
643+
return new MetadataInvariantData(
644+
metadataReader.GetTableRowCount(TableIndex.TypeDef),
645+
metadataReader.GetTableRowCount(TableIndex.MethodDef),
646+
metadataReader.GetTableRowCount(TableIndex.Field),
647+
metadataReader.TypeDefinitions.Select(handle => metadataReader.GetString(metadataReader.GetTypeDefinition(handle).Name)).ToArray(),
648+
metadataReader.MethodDefinitions.Select(handle => metadataReader.GetString(metadataReader.GetMethodDefinition(handle).Name)).ToArray());
649+
}
650+
651+
private readonly record struct MetadataInvariantData(
652+
int TypeDefinitionRowCount,
653+
int MethodDefinitionRowCount,
654+
int FieldDefinitionRowCount,
655+
string[] TypeDefinitionNamesInMetadataOrder,
656+
string[] MethodDefinitionNamesInMetadataOrder);
657+
564658
private sealed class NonSeekableWriteStream : Stream
565659
{
566660
private readonly Stream _innerStream;

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,42 @@ public void EnumBuilderSetCustomAttributesTest()
510510
}
511511
}
512512

513+
[Fact]
514+
public void NamedArgumentsOnlyEnumArrayCustomAttributeRoundTrips()
515+
{
516+
using (TempFile file = TempFile.Create())
517+
{
518+
PersistedAssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(new AssemblyName("NamedArgumentsAttributeAssembly"));
519+
ModuleBuilder module = ab.DefineDynamicModule("Module");
520+
TypeBuilder typeBuilder = module.DefineType("AttributedType", TypeAttributes.Public | TypeAttributes.Class);
521+
522+
ConstructorInfo ctor = typeof(NamedArgumentsOnlyAttribute).GetConstructor(Type.EmptyTypes);
523+
PropertyInfo modesProperty = typeof(NamedArgumentsOnlyAttribute).GetProperty(nameof(NamedArgumentsOnlyAttribute.Modes));
524+
PropertyInfo primaryModeProperty = typeof(NamedArgumentsOnlyAttribute).GetProperty(nameof(NamedArgumentsOnlyAttribute.PrimaryMode));
525+
CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(
526+
ctor,
527+
Array.Empty<object>(),
528+
new[] { modesProperty, primaryModeProperty },
529+
new object[] { new[] { AttributeEdgeMode.Second, AttributeEdgeMode.First }, AttributeEdgeMode.Second });
530+
531+
typeBuilder.SetCustomAttribute(attributeBuilder);
532+
typeBuilder.CreateType();
533+
ab.Save(file.Path);
534+
535+
using MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver());
536+
Type typeFromDisk = mlc.LoadFromAssemblyPath(file.Path).GetType("AttributedType");
537+
CustomAttributeData attributeFromDisk = typeFromDisk.GetCustomAttributesData().Single(c => c.AttributeType.Name == nameof(NamedArgumentsOnlyAttribute));
538+
539+
CustomAttributeNamedArgument modesNamedArgument = attributeFromDisk.NamedArguments.Single(na => na.MemberName == nameof(NamedArgumentsOnlyAttribute.Modes));
540+
IList<CustomAttributeTypedArgument> modes = (IList<CustomAttributeTypedArgument>)modesNamedArgument.TypedValue.Value;
541+
Assert.Equal((int)AttributeEdgeMode.Second, (int)modes[0].Value);
542+
Assert.Equal((int)AttributeEdgeMode.First, (int)modes[1].Value);
543+
544+
CustomAttributeNamedArgument primaryModeNamedArgument = attributeFromDisk.NamedArguments.Single(na => na.MemberName == nameof(NamedArgumentsOnlyAttribute.PrimaryMode));
545+
Assert.Equal((int)AttributeEdgeMode.Second, (int)primaryModeNamedArgument.TypedValue.Value);
546+
}
547+
}
548+
513549
private void AssertEnumAttributes(string fullName, object value, CustomAttributeData testAttribute)
514550
{
515551
Assert.Equal(fullName, testAttribute.AttributeType.FullName);
@@ -522,4 +558,16 @@ public class BoolAttribute : Attribute
522558
private bool _b;
523559
public BoolAttribute(bool myBool) { _b = myBool; }
524560
}
561+
562+
public enum AttributeEdgeMode
563+
{
564+
First,
565+
Second
566+
}
567+
568+
public class NamedArgumentsOnlyAttribute : Attribute
569+
{
570+
public AttributeEdgeMode[] Modes { get; set; } = [];
571+
public AttributeEdgeMode PrimaryMode { get; set; }
572+
}
525573
}

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3066,5 +3066,47 @@ public void ReferenceGenericMembersInOtherGeneratedAssembly()
30663066
tlc.Unload();
30673067
}
30683068
}
3069+
3070+
[Fact]
3071+
public void ReferenceNestedGenericTypeWithConstraintAcrossGeneratedAssemblies()
3072+
{
3073+
using (TempFile file = TempFile.Create())
3074+
using (TempFile file2 = TempFile.Create())
3075+
{
3076+
PersistedAssemblyBuilder producerAssembly = new PersistedAssemblyBuilder(new AssemblyName("ProducerAssembly"), typeof(object).Assembly);
3077+
ModuleBuilder producerModule = producerAssembly.DefineDynamicModule("ProducerModule");
3078+
TypeBuilder hostType = producerModule.DefineType("HostType", TypeAttributes.Public | TypeAttributes.Class);
3079+
GenericTypeParameterBuilder hostGenericParameter = hostType.DefineGenericParameters("T")[0];
3080+
hostGenericParameter.SetGenericParameterAttributes(GenericParameterAttributes.ReferenceTypeConstraint);
3081+
hostType.DefineDefaultConstructor(MethodAttributes.Public);
3082+
hostType.CreateType();
3083+
producerAssembly.Save(file.Path);
3084+
3085+
PersistedAssemblyBuilder consumerAssembly = new PersistedAssemblyBuilder(new AssemblyName("ConsumerAssembly"), typeof(object).Assembly);
3086+
ModuleBuilder consumerModule = consumerAssembly.DefineDynamicModule("ConsumerModule");
3087+
TypeBuilder consumerType = consumerModule.DefineType("ConsumerType", TypeAttributes.Public | TypeAttributes.Class);
3088+
3089+
Type constructedHostType = hostType.MakeGenericType(typeof(string));
3090+
Type nestedGenericReturnType = typeof(List<>).MakeGenericType(constructedHostType);
3091+
MethodBuilder factoryMethod = consumerType.DefineMethod("CreateList", MethodAttributes.Public | MethodAttributes.Static, nestedGenericReturnType, Type.EmptyTypes);
3092+
ILGenerator il = factoryMethod.GetILGenerator();
3093+
il.Emit(OpCodes.Ldnull);
3094+
il.Emit(OpCodes.Ret);
3095+
consumerType.CreateType();
3096+
consumerAssembly.Save(file2.Path);
3097+
3098+
TestAssemblyLoadContext tlc = new TestAssemblyLoadContext();
3099+
tlc.LoadFromAssemblyPath(file.Path);
3100+
Type consumerTypeFromDisk = tlc.LoadFromAssemblyPath(file2.Path).GetType("ConsumerType");
3101+
MethodInfo createListMethod = consumerTypeFromDisk.GetMethod("CreateList");
3102+
Type listElementType = createListMethod.ReturnType.GetGenericArguments()[0];
3103+
3104+
Assert.True(listElementType.IsGenericType);
3105+
Assert.Equal("HostType", listElementType.Name);
3106+
Assert.Equal(typeof(string).FullName, listElementType.GetGenericArguments()[0].FullName);
3107+
Assert.Equal("ProducerAssembly", listElementType.Assembly.GetName().Name);
3108+
tlc.Unload();
3109+
}
3110+
}
30693111
}
30703112
}

src/libraries/System.Reflection.Emit/tests/PersistedAssemblyBuilder/MissingScenarios.txt

Lines changed: 0 additions & 45 deletions
This file was deleted.

0 commit comments

Comments
 (0)