Skip to content

Commit 16f2675

Browse files
authored
Fix alignment padding and add test for saving managed resources (#110915)
* Add test for saving and reading managed resources * Use TempFile helper for tests; align Resource section * Verify ManagedPEBuilder.Serialize() doesn't modify incoming resource blob for padding * Remove Assert; test using LoadContext instead of RemoteExecutor * Remove Asserts and padding * Remove unnecessary using statement
1 parent 01e4d44 commit 16f2675

File tree

4 files changed

+168
-70
lines changed

4 files changed

+168
-70
lines changed

src/libraries/System.Reflection.Emit/tests/AssemblyBuilderTests.cs

Lines changed: 64 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -411,100 +411,100 @@ private static void VerifyAssemblyBuilder(AssemblyBuilder assembly, AssemblyName
411411
Assert.Empty(assembly.GetTypes());
412412
}
413413

414-
private static void SamplePrivateMethod ()
415-
{
416-
}
414+
private static void SamplePrivateMethod()
415+
{
416+
}
417417

418-
internal static void SampleInternalMethod ()
419-
{
420-
}
418+
internal static void SampleInternalMethod()
419+
{
420+
}
421421

422-
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))]
423-
void Invoke_Private_CrossAssembly_ThrowsMethodAccessException()
424-
{
425-
TypeBuilder tb = Helpers.DynamicType(TypeAttributes.Public);
426-
var mb = tb.DefineMethod ("MyMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(void), new Type[] { });
422+
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))]
423+
void Invoke_Private_CrossAssembly_ThrowsMethodAccessException()
424+
{
425+
TypeBuilder tb = Helpers.DynamicType(TypeAttributes.Public);
426+
var mb = tb.DefineMethod("MyMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(void), new Type[] { });
427427

428-
var ilg = mb.GetILGenerator ();
428+
var ilg = mb.GetILGenerator();
429429

430-
var callee = typeof (AssemblyTests).GetMethod ("SamplePrivateMethod", BindingFlags.Static | BindingFlags.NonPublic);
430+
var callee = typeof(AssemblyTests).GetMethod("SamplePrivateMethod", BindingFlags.Static | BindingFlags.NonPublic);
431431

432-
ilg.Emit (OpCodes.Call, callee);
433-
ilg.Emit (OpCodes.Ret);
432+
ilg.Emit(OpCodes.Call, callee);
433+
ilg.Emit(OpCodes.Ret);
434434

435-
var ty = tb.CreateType ();
435+
var ty = tb.CreateType();
436436

437-
var mi = ty.GetMethod ("MyMethod", BindingFlags.Static | BindingFlags.Public);
437+
var mi = ty.GetMethod("MyMethod", BindingFlags.Static | BindingFlags.Public);
438438

439-
var d = (Action) mi.CreateDelegate (typeof(Action));
439+
var d = (Action)mi.CreateDelegate(typeof(Action));
440440

441-
Assert.Throws<MethodAccessException>(() => d ());
442-
}
441+
Assert.Throws<MethodAccessException>(() => d());
442+
}
443443

444-
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))]
445-
void Invoke_Internal_CrossAssembly_ThrowsMethodAccessException()
446-
{
447-
TypeBuilder tb = Helpers.DynamicType(TypeAttributes.Public);
448-
var mb = tb.DefineMethod ("MyMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(void), new Type[] { });
444+
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))]
445+
void Invoke_Internal_CrossAssembly_ThrowsMethodAccessException()
446+
{
447+
TypeBuilder tb = Helpers.DynamicType(TypeAttributes.Public);
448+
var mb = tb.DefineMethod("MyMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(void), new Type[] { });
449449

450-
var ilg = mb.GetILGenerator ();
450+
var ilg = mb.GetILGenerator();
451451

452-
var callee = typeof (AssemblyTests).GetMethod ("SampleInternalMethod", BindingFlags.Static | BindingFlags.NonPublic);
452+
var callee = typeof(AssemblyTests).GetMethod("SampleInternalMethod", BindingFlags.Static | BindingFlags.NonPublic);
453453

454-
ilg.Emit (OpCodes.Call, callee);
455-
ilg.Emit (OpCodes.Ret);
454+
ilg.Emit(OpCodes.Call, callee);
455+
ilg.Emit(OpCodes.Ret);
456456

457-
var ty = tb.CreateType ();
457+
var ty = tb.CreateType();
458458

459-
var mi = ty.GetMethod ("MyMethod", BindingFlags.Static | BindingFlags.Public);
459+
var mi = ty.GetMethod("MyMethod", BindingFlags.Static | BindingFlags.Public);
460460

461-
var d = (Action) mi.CreateDelegate (typeof(Action));
461+
var d = (Action)mi.CreateDelegate(typeof(Action));
462462

463-
Assert.Throws<MethodAccessException>(() => d ());
464-
}
463+
Assert.Throws<MethodAccessException>(() => d());
464+
}
465465

466-
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))]
467-
void Invoke_Private_SameAssembly_ThrowsMethodAccessException()
468-
{
469-
ModuleBuilder modb = Helpers.DynamicModule();
466+
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))]
467+
void Invoke_Private_SameAssembly_ThrowsMethodAccessException()
468+
{
469+
ModuleBuilder modb = Helpers.DynamicModule();
470470

471-
string calleeName = "PrivateMethod";
471+
string calleeName = "PrivateMethod";
472472

473-
TypeBuilder tbCalled = modb.DefineType ("CalledClass", TypeAttributes.Public);
474-
var mbCalled = tbCalled.DefineMethod (calleeName, MethodAttributes.Private | MethodAttributes.Static);
475-
mbCalled.GetILGenerator().Emit (OpCodes.Ret);
473+
TypeBuilder tbCalled = modb.DefineType("CalledClass", TypeAttributes.Public);
474+
var mbCalled = tbCalled.DefineMethod(calleeName, MethodAttributes.Private | MethodAttributes.Static);
475+
mbCalled.GetILGenerator().Emit(OpCodes.Ret);
476476

477-
var tyCalled = tbCalled.CreateType();
478-
var callee = tyCalled.GetMethod (calleeName, BindingFlags.NonPublic | BindingFlags.Static);
477+
var tyCalled = tbCalled.CreateType();
478+
var callee = tyCalled.GetMethod(calleeName, BindingFlags.NonPublic | BindingFlags.Static);
479479

480-
TypeBuilder tb = modb.DefineType("CallerClass", TypeAttributes.Public);
481-
var mb = tb.DefineMethod ("MyMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(void), new Type[] { });
480+
TypeBuilder tb = modb.DefineType("CallerClass", TypeAttributes.Public);
481+
var mb = tb.DefineMethod("MyMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(void), new Type[] { });
482482

483-
var ilg = mb.GetILGenerator ();
483+
var ilg = mb.GetILGenerator();
484484

485-
ilg.Emit (OpCodes.Call, callee);
486-
ilg.Emit (OpCodes.Ret);
485+
ilg.Emit(OpCodes.Call, callee);
486+
ilg.Emit(OpCodes.Ret);
487487

488-
var ty = tb.CreateType ();
488+
var ty = tb.CreateType();
489489

490-
var mi = ty.GetMethod ("MyMethod", BindingFlags.Static | BindingFlags.Public);
490+
var mi = ty.GetMethod("MyMethod", BindingFlags.Static | BindingFlags.Public);
491491

492-
var d = (Action) mi.CreateDelegate (typeof(Action));
492+
var d = (Action)mi.CreateDelegate(typeof(Action));
493493

494-
Assert.Throws<MethodAccessException>(() => d ());
495-
}
494+
Assert.Throws<MethodAccessException>(() => d());
495+
}
496496

497-
[Fact]
498-
public void DefineDynamicAssembly_AssemblyBuilderLocationIsEmpty_InternalAssemblyBuilderLocationIsEmpty()
499-
{
500-
AssemblyBuilder assembly = Helpers.DynamicAssembly(nameof(DefineDynamicAssembly_AssemblyBuilderLocationIsEmpty_InternalAssemblyBuilderLocationIsEmpty));
501-
Assembly internalAssemblyBuilder = AppDomain.CurrentDomain.GetAssemblies()
502-
.FirstOrDefault(a => a.FullName == assembly.FullName);
497+
[Fact]
498+
public void DefineDynamicAssembly_AssemblyBuilderLocationIsEmpty_InternalAssemblyBuilderLocationIsEmpty()
499+
{
500+
AssemblyBuilder assembly = Helpers.DynamicAssembly(nameof(DefineDynamicAssembly_AssemblyBuilderLocationIsEmpty_InternalAssemblyBuilderLocationIsEmpty));
501+
Assembly internalAssemblyBuilder = AppDomain.CurrentDomain.GetAssemblies()
502+
.FirstOrDefault(a => a.FullName == assembly.FullName);
503503

504-
Assert.Empty(assembly.Location);
505-
Assert.NotNull(internalAssemblyBuilder);
506-
Assert.Empty(internalAssemblyBuilder.Location);
507-
}
504+
Assert.Empty(assembly.Location);
505+
Assert.NotNull(internalAssemblyBuilder);
506+
Assert.Empty(internalAssemblyBuilder.Location);
507+
}
508508

509509
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
510510
public static void ThrowsWhenDynamicCodeNotSupported()
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections;
5+
using System.Globalization;
6+
using System.IO;
7+
using System.Reflection.Metadata;
8+
using System.Reflection.Metadata.Ecma335;
9+
using System.Reflection.PortableExecutable;
10+
using System.Resources;
11+
using Xunit;
12+
13+
namespace System.Reflection.Emit.Tests
14+
{
15+
[ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
16+
public class AssemblySaveResourceTests
17+
{
18+
[Theory]
19+
[InlineData(new byte[] { 1 })]
20+
[InlineData(new byte[] { 1, 2 })] // Verify blob alignment padding by adding a byte.
21+
public void ManagedResourcesAndFieldData(byte[] byteValues)
22+
{
23+
PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(new AssemblyName("MyAssemblyWithResource"), typeof(object).Assembly);
24+
ab.DefineDynamicModule("MyModule");
25+
MetadataBuilder metadata = ab.GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder fieldData);
26+
27+
// We shouldn't have any field data.
28+
Assert.Equal(0, fieldData.Count);
29+
fieldData = new ();
30+
fieldData.WriteBytes(byteValues);
31+
32+
using MemoryStream memoryStream = new MemoryStream();
33+
ResourceWriter myResourceWriter = new ResourceWriter(memoryStream);
34+
myResourceWriter.AddResource("StringResource", "Value");
35+
myResourceWriter.AddResource("ByteResource", byteValues);
36+
myResourceWriter.Close();
37+
38+
byte[] data = memoryStream.ToArray();
39+
BlobBuilder resourceBlob = new BlobBuilder();
40+
resourceBlob.WriteInt32(data.Length);
41+
resourceBlob.WriteBytes(data);
42+
int resourceBlobSize = resourceBlob.Count;
43+
44+
metadata.AddManifestResource(
45+
ManifestResourceAttributes.Public,
46+
metadata.GetOrAddString("MyResource.resources"),
47+
implementation: default,
48+
offset: 0);
49+
50+
ManagedPEBuilder peBuilder = new ManagedPEBuilder(
51+
header: PEHeaderBuilder.CreateLibraryHeader(),
52+
metadataRootBuilder: new MetadataRootBuilder(metadata),
53+
ilStream: ilStream,
54+
mappedFieldData: fieldData,
55+
managedResources: resourceBlob);
56+
57+
BlobBuilder blob = new BlobBuilder();
58+
peBuilder.Serialize(blob);
59+
60+
// Ensure the the blobs passed to Serialize() weren't modified due to alignment padding
61+
Assert.Equal(resourceBlobSize, resourceBlob.Count);
62+
Assert.Equal(byteValues.Length, fieldData.Count);
63+
64+
// To verify the resources work with runtime APIs, load the assembly into the process instead of
65+
// the normal testing approach of using MetadataLoadContext.
66+
TestAssemblyLoadContext testAssemblyLoadContext = new();
67+
try
68+
{
69+
Assembly readAssembly = testAssemblyLoadContext.LoadFromStream(new MemoryStream(blob.ToArray()));
70+
71+
// Use ResourceReader to read the resources.
72+
using Stream readStream = readAssembly.GetManifestResourceStream("MyResource.resources")!;
73+
using ResourceReader reader = new(readStream);
74+
Verify(reader.GetEnumerator());
75+
76+
// Use ResourceManager to read the resources.
77+
ResourceManager rm = new ResourceManager("MyResource", readAssembly);
78+
ResourceSet resourceSet = rm.GetResourceSet(CultureInfo.InvariantCulture, createIfNotExists: true, tryParents: false);
79+
Verify(resourceSet.GetEnumerator());
80+
}
81+
finally
82+
{
83+
testAssemblyLoadContext.Unload();
84+
}
85+
86+
void Verify(IDictionaryEnumerator resources)
87+
{
88+
Assert.True(resources.MoveNext());
89+
DictionaryEntry resource = (DictionaryEntry)resources.Current;
90+
Assert.Equal("ByteResource", resource.Key);
91+
Assert.Equal(byteValues, resource.Value);
92+
93+
Assert.True(resources.MoveNext());
94+
resource = (DictionaryEntry)resources.Current;
95+
Assert.Equal("StringResource", resource.Key);
96+
Assert.Equal("Value", resource.Value);
97+
98+
Assert.False(resources.MoveNext());
99+
}
100+
}
101+
}
102+
}
103+

src/libraries/System.Reflection.Emit/tests/System.Reflection.Emit.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
<Compile Include="PersistedAssemblyBuilder\AssemblySaveTypeBuilderAPIsTests.cs" />
7272
<Compile Include="PersistedAssemblyBuilder\AssemblySaveModuleBuilderTests.cs" />
7373
<Compile Include="PersistedAssemblyBuilder\AssemblySavePropertyBuilderTests.cs" />
74+
<Compile Include="PersistedAssemblyBuilder\AssemblySaveResourceTests.cs" />
7475
<Compile Include="PersistedAssemblyBuilder\AssemblySaveTools.cs" />
7576
<Compile Include="PersistedAssemblyBuilder\AssemblySaveTypeBuilderTests.cs" />
7677
<Compile Include="PortablePdb\ILGeneratorScopesAndSequencePointsTests.cs" />

src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/ManagedTextSection.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ internal sealed class ManagedTextSection
4141

4242
/// <summary>
4343
/// The size of managed resource data stream.
44-
/// Aligned to <see cref="ManagedResourcesDataAlignment"/>.
4544
/// </summary>
4645
public int ResourceDataSize { get; }
4746

@@ -147,9 +146,6 @@ public int CalculateOffsetToMappedFieldDataStream()
147146

148147
internal int ComputeOffsetToDebugDirectory()
149148
{
150-
Debug.Assert(MetadataSize % 4 == 0);
151-
Debug.Assert(ResourceDataSize % 4 == 0);
152-
153149
return
154150
ComputeOffsetToMetadata() +
155151
MetadataSize +
@@ -187,7 +183,6 @@ private int ComputeOffsetToMetadata()
187183

188184
public int ComputeSizeOfTextSection()
189185
{
190-
Debug.Assert(MappedFieldDataSize % MappedFieldDataAlignment == 0);
191186
return CalculateOffsetToMappedFieldDataStream() + MappedFieldDataSize;
192187
}
193188

@@ -254,7 +249,6 @@ public void Serialize(
254249
Debug.Assert(ilBuilder.Count == ILStreamSize);
255250
Debug.Assert((mappedFieldDataBuilderOpt?.Count ?? 0) == MappedFieldDataSize);
256251
Debug.Assert((resourceBuilderOpt?.Count ?? 0) == ResourceDataSize);
257-
Debug.Assert((resourceBuilderOpt?.Count ?? 0) % 4 == 0);
258252

259253
// TODO: avoid recalculation
260254
int importTableRva = GetImportTableDirectoryEntry(relativeVirtualAddess).RelativeVirtualAddress;

0 commit comments

Comments
 (0)