@@ -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 ;
0 commit comments