Skip to content

Commit bd84a96

Browse files
buyaa-nstephentoubjkotas
authored
Add public APIs for persisted AssemblyBulder (#97177)
* Add public APIs for persisted AssemblyBuilder * Update tests to use public API, refactor tests so that the temp files deleted * Make AB.SaveCore(Stream) virtual and add meaningful message for excepiton thrown Co-authored-by: Stephen Toub <[email protected]> Co-authored-by: Jan Kotas <[email protected]> * Remove unneeded message * Update newer tests to use new public APIs --------- Co-authored-by: Stephen Toub <[email protected]> Co-authored-by: Jan Kotas <[email protected]>
1 parent b1e85b8 commit bd84a96

18 files changed

+1357
-1181
lines changed

src/libraries/System.Private.CoreLib/src/Resources/Strings.resx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4289,4 +4289,7 @@
42894289
<data name="Reflection_Disabled" xml:space="preserve">
42904290
<value>This operation is not available because the reflection support was disabled at compile time.</value>
42914291
</data>
4292-
</root>
4292+
<data name="NotSupported_AssemblySave" xml:space="preserve">
4293+
<value>This AssemblyBuilder instance doesn't support saving. Use AssemblyBuilder.DefinePersistedAssembly to create an AssemblyBuilder instance that supports saving.</value>
4294+
</data>
4295+
</root>

src/libraries/System.Private.CoreLib/src/System/Reflection/Emit/AssemblyBuilder.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Collections.Generic;
45
using System.Diagnostics.CodeAnalysis;
56
using System.IO;
67
using System.Runtime.CompilerServices;
@@ -32,8 +33,56 @@ public ModuleBuilder DefineDynamicModule(string name)
3233
return GetDynamicModuleCore(name);
3334
}
3435

36+
/// <summary>
37+
/// Defines an <see cref="AssemblyBuilder"/> that can be saved to a file or stream.
38+
/// </summary>
39+
/// <param name="name">The name of the assembly.</param>
40+
/// <param name="coreAssembly">The assembly that denotes the "system assembly" that houses the well-known types such as <see cref="object"/></param>
41+
/// <param name="assemblyAttributes">A collection that contains the attributes of the assembly.</param>
42+
/// <returns>An <see cref="AssemblyBuilder"/> that can be persisted.</returns>
43+
/// <exception cref="ArgumentNullException">The <paramref name="name"/> or <paramref name="name.Name"/> or <paramref name="coreAssembly"/> is null.</exception>
44+
/// <remarks>Currently the persisted assembly doesn't support running, need to save it and load back to run.</remarks>
45+
public static AssemblyBuilder DefinePersistedAssembly(AssemblyName name, Assembly coreAssembly, IEnumerable<CustomAttributeBuilder>? assemblyAttributes = null)
46+
{
47+
ArgumentNullException.ThrowIfNull(name);
48+
ArgumentException.ThrowIfNullOrEmpty(name.Name, "AssemblyName.Name");
49+
ArgumentNullException.ThrowIfNull(coreAssembly);
50+
51+
Type assemblyType = Type.GetType("System.Reflection.Emit.AssemblyBuilderImpl, System.Reflection.Emit", throwOnError: true)!;
52+
ConstructorInfo con = assemblyType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, [typeof(AssemblyName), typeof(Assembly), typeof(IEnumerable<CustomAttributeBuilder>)])!;
53+
return (AssemblyBuilder)con.Invoke([name, coreAssembly, assemblyAttributes]);
54+
}
55+
3556
protected abstract ModuleBuilder? GetDynamicModuleCore(string name);
3657

58+
/// <summary>
59+
/// Serializes the assembly to <see cref="Stream"/>.
60+
/// </summary>
61+
/// <param name="stream">The <see cref="Stream"/> to which the assembly serialized.</param>
62+
/// <exception cref="ArgumentNullException"><paramref name="stream"/> is null.</exception>
63+
/// <exception cref="NotSupportedException">The AssemblyBuilder instance doesn't support saving.</exception>
64+
public void Save(Stream stream) => SaveCore(stream);
65+
66+
/// <summary>
67+
/// Saves the assembly to disk.
68+
/// </summary>
69+
/// <param name="assemblyFileName">The file name of the assembly.</param>
70+
/// <exception cref="ArgumentNullException"><paramref name="assemblyFileName"/> is null.</exception>
71+
/// <exception cref="NotSupportedException">The AssemblyBuilder instance doesn't support saving.</exception>
72+
public void Save(string assemblyFileName)
73+
{
74+
ArgumentNullException.ThrowIfNull(assemblyFileName);
75+
76+
using var peStream = new FileStream(assemblyFileName, FileMode.Create, FileAccess.Write);
77+
SaveCore(peStream);
78+
}
79+
80+
/// <summary>
81+
/// When implemented in a derived type, serializes the assembly to a stream.
82+
/// </summary>
83+
/// <param name="stream">The stream to which the assembly serialized.</param>
84+
protected virtual void SaveCore(Stream stream) => throw new NotSupportedException(SR.NotSupported_AssemblySave);
85+
3786
public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute)
3887
{
3988
ArgumentNullException.ThrowIfNull(con);

src/libraries/System.Reflection.Emit/ref/System.Reflection.Emit.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ protected AssemblyBuilder() { }
2424
public static System.Reflection.Emit.AssemblyBuilder DefineDynamicAssembly(System.Reflection.AssemblyName name, System.Reflection.Emit.AssemblyBuilderAccess access) { throw null; }
2525
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Defining a dynamic assembly requires dynamic code.")]
2626
public static System.Reflection.Emit.AssemblyBuilder DefineDynamicAssembly(System.Reflection.AssemblyName name, System.Reflection.Emit.AssemblyBuilderAccess access, System.Collections.Generic.IEnumerable<System.Reflection.Emit.CustomAttributeBuilder>? assemblyAttributes) { throw null; }
27+
public static System.Reflection.Emit.AssemblyBuilder DefinePersistedAssembly(System.Reflection.AssemblyName name, System.Reflection.Assembly coreAssembly, System.Collections.Generic.IEnumerable<System.Reflection.Emit.CustomAttributeBuilder>? assemblyAttributes = null) { throw null; }
2728
public System.Reflection.Emit.ModuleBuilder DefineDynamicModule(string name) { throw null; }
2829
protected abstract System.Reflection.Emit.ModuleBuilder DefineDynamicModuleCore(string name);
2930
public override bool Equals(object? obj) { throw null; }
@@ -54,6 +55,9 @@ protected AssemblyBuilder() { }
5455
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Types might be removed by trimming. If the type name is a string literal, consider using Type.GetType instead.")]
5556
public override System.Type? GetType(string name, bool throwOnError, bool ignoreCase) { throw null; }
5657
public override bool IsDefined(System.Type attributeType, bool inherit) { throw null; }
58+
public void Save(string assemblyFileName) { throw null; }
59+
public void Save(System.IO.Stream stream) { throw null; }
60+
protected virtual void SaveCore(System.IO.Stream stream) { }
5761
public void SetCustomAttribute(System.Reflection.ConstructorInfo con, byte[] binaryAttribute) { }
5862
public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { }
5963
protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan<byte> binaryAttribute);
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
<linker>
22
<assembly fullname="System.Reflection.Emit">
33
<type fullname="System.Reflection.Emit.AssemblyBuilderImpl">
4-
<!-- Internal API used by tests only. -->
5-
<method name="DefinePersistedAssembly" />
6-
<method name="Save" />
4+
<!-- Internal API called through Reflection by another assembly. -->
5+
<method name=".ctor" />
76
</type>
87
</assembly>
98
</linker>

src/libraries/System.Reflection.Emit/src/System/Reflection/Emit/AssemblyBuilderImpl.cs

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,9 @@ internal sealed class AssemblyBuilderImpl : AssemblyBuilder
1919

2020
internal List<CustomAttributeWrapper>? _customAttributes;
2121

22-
internal AssemblyBuilderImpl(AssemblyName name, Assembly coreAssembly, IEnumerable<CustomAttributeBuilder>? assemblyAttributes)
22+
internal AssemblyBuilderImpl(AssemblyName name, Assembly coreAssembly, IEnumerable<CustomAttributeBuilder>? assemblyAttributes = null)
2323
{
24-
ArgumentNullException.ThrowIfNull(name);
25-
26-
name = (AssemblyName)name.Clone();
27-
28-
ArgumentException.ThrowIfNullOrEmpty(name.Name, "AssemblyName.Name");
29-
30-
_assemblyName = name;
24+
_assemblyName = (AssemblyName)name.Clone();
3125
_coreAssembly = coreAssembly;
3226
_metadataBuilder = new MetadataBuilder();
3327

@@ -40,10 +34,6 @@ internal AssemblyBuilderImpl(AssemblyName name, Assembly coreAssembly, IEnumerab
4034
}
4135
}
4236

43-
internal static AssemblyBuilderImpl DefinePersistedAssembly(AssemblyName name, Assembly coreAssembly,
44-
IEnumerable<CustomAttributeBuilder>? assemblyAttributes)
45-
=> new AssemblyBuilderImpl(name, coreAssembly, assemblyAttributes);
46-
4737
private void WritePEImage(Stream peStream, BlobBuilder ilBuilder, BlobBuilder fieldData)
4838
{
4939
var peHeaderBuilder = new PEHeaderBuilder(
@@ -64,7 +54,7 @@ private void WritePEImage(Stream peStream, BlobBuilder ilBuilder, BlobBuilder fi
6454
peBlob.WriteContentTo(peStream);
6555
}
6656

67-
internal void Save(Stream stream)
57+
protected override void SaveCore(Stream stream)
6858
{
6959
ArgumentNullException.ThrowIfNull(stream);
7060

@@ -103,14 +93,6 @@ internal void Save(Stream stream)
10393
private static AssemblyFlags AddContentType(AssemblyFlags flags, AssemblyContentType contentType)
10494
=> (AssemblyFlags)((int)contentType << 9) | flags;
10595

106-
internal void Save(string assemblyFileName)
107-
{
108-
ArgumentNullException.ThrowIfNull(assemblyFileName);
109-
110-
using var peStream = new FileStream(assemblyFileName, FileMode.Create, FileAccess.Write);
111-
Save(peStream);
112-
}
113-
11496
protected override ModuleBuilder DefineDynamicModuleCore(string name)
11597
{
11698
if (_module != null)

src/libraries/System.Reflection.Emit/tests/PersistableAssemblyBuilder/AssemblySaveAssemblyBuilder.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public void AssemblyWithDifferentTypes()
3434
aName.CultureInfo = new CultureInfo("en");
3535
aName.Flags = AssemblyNameFlags.Retargetable;
3636

37-
AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilderAndSaveMethod(aName, null, typeof(string), out MethodInfo saveMethod);
37+
AssemblyBuilder ab = AssemblySaveTools.PopulateAssemblyBuilder(aName);
3838

3939
ab.SetCustomAttribute(new CustomAttributeBuilder(typeof(AssemblyDelaySignAttribute).GetConstructor([typeof(bool)]), [true]));
4040

@@ -213,10 +213,12 @@ public void AssemblyWithDifferentTypes()
213213
eventb.SetRemoveOnMethod(mbRemove);
214214
tbEvents.CreateType();
215215

216-
saveMethod.Invoke(ab, [file.Path]);
216+
ab.Save(file.Path);
217217

218-
Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path);
219-
CheckAssembly(assemblyFromDisk);
218+
using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver()))
219+
{
220+
CheckAssembly(mlc.LoadFromAssemblyPath(file.Path));
221+
}
220222
}
221223
}
222224

0 commit comments

Comments
 (0)