Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add public APIs for persisted AssemblyBulder #97177

Merged
merged 10 commits into from
Jan 21, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -4289,4 +4289,7 @@
<data name="Reflection_Disabled" xml:space="preserve">
<value>This operation is not available because the reflection support was disabled at compile time.</value>
</data>
</root>
<data name="NotSupported_AssemblySave" xml:space="preserve">
<value>This AssemblyBuilder instance doesn't support saving. Use AssemblyBuilder.DefinePersistedAssembly to create an AssemblyBuilder instance that supports saving.</value>
</data>
</root>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -32,8 +33,56 @@ public ModuleBuilder DefineDynamicModule(string name)
return GetDynamicModuleCore(name);
}

/// <summary>
/// Defines an <see cref="AssemblyBuilder"/> that can be saved to a file or stream.
/// </summary>
/// <param name="name">The name of the assembly.</param>
/// <param name="coreAssembly">The assembly that denotes the "system assembly" that houses the well-known types such as <see cref="object"/></param>
/// <param name="assemblyAttributes">A collection that contains the attributes of the assembly.</param>
/// <returns>An <see cref="AssemblyBuilder"/> that can be persisted.</returns>
/// <exception cref="ArgumentNullException">The <paramref name="name"/> or <paramref name="name.Name"/> or <paramref name="coreAssembly"/> is null.</exception>
/// <remarks>Currently the persisted assembly doesn't support running, need to save it and load back to run.</remarks>
public static AssemblyBuilder DefinePersistedAssembly(AssemblyName name, Assembly coreAssembly, IEnumerable<CustomAttributeBuilder>? assemblyAttributes = null)
{
ArgumentNullException.ThrowIfNull(name);
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
ArgumentException.ThrowIfNullOrEmpty(name.Name, "AssemblyName.Name");
ArgumentNullException.ThrowIfNull(coreAssembly);

Type assemblyType = Type.GetType("System.Reflection.Emit.AssemblyBuilderImpl, System.Reflection.Emit", throwOnError: true)!;
ConstructorInfo con = assemblyType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, [typeof(AssemblyName), typeof(Assembly), typeof(IEnumerable<CustomAttributeBuilder>)])!;
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
return (AssemblyBuilder)con.Invoke([name, coreAssembly, assemblyAttributes]);
}

protected abstract ModuleBuilder? GetDynamicModuleCore(string name);

/// <summary>
/// Serializes the assembly to <see cref="Stream"/>.
/// </summary>
/// <param name="stream">The <see cref="Stream"/> to which the assembly serialized.</param>
/// <exception cref="ArgumentNullException"><paramref name="stream"/> is null.</exception>
/// <exception cref="NotSupportedException">The AssemblyBuilder instance doesn't support saving.</exception>
public void Save(Stream stream) => SaveCore(stream);

/// <summary>
/// Saves the assembly to disk.
/// </summary>
/// <param name="assemblyFileName">The file name of the assembly.</param>
/// <exception cref="ArgumentNullException"><paramref name="assemblyFileName"/> is null.</exception>
/// <exception cref="NotSupportedException">The AssemblyBuilder instance doesn't support saving.</exception>
public void Save(string assemblyFileName)
{
ArgumentNullException.ThrowIfNull(assemblyFileName);

using var peStream = new FileStream(assemblyFileName, FileMode.Create, FileAccess.Write);
SaveCore(peStream);
}

/// <summary>
/// When implemented in a derived type, serializes the assembly to a stream.
/// </summary>
/// <param name="stream">The stream to which the assembly serialized.</param>
protected virtual void SaveCore(Stream stream) => throw new NotSupportedException(SR.NotSupported_AssemblySave);

public void SetCustomAttribute(ConstructorInfo con, byte[] binaryAttribute)
{
ArgumentNullException.ThrowIfNull(con);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ protected AssemblyBuilder() { }
public static System.Reflection.Emit.AssemblyBuilder DefineDynamicAssembly(System.Reflection.AssemblyName name, System.Reflection.Emit.AssemblyBuilderAccess access) { throw null; }
[System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Defining a dynamic assembly requires dynamic code.")]
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; }
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; }
public System.Reflection.Emit.ModuleBuilder DefineDynamicModule(string name) { throw null; }
protected abstract System.Reflection.Emit.ModuleBuilder DefineDynamicModuleCore(string name);
public override bool Equals(object? obj) { throw null; }
Expand Down Expand Up @@ -54,6 +55,9 @@ protected AssemblyBuilder() { }
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Types might be removed by trimming. If the type name is a string literal, consider using Type.GetType instead.")]
public override System.Type? GetType(string name, bool throwOnError, bool ignoreCase) { throw null; }
public override bool IsDefined(System.Type attributeType, bool inherit) { throw null; }
public void Save(string assemblyFileName) { throw null; }
public void Save(System.IO.Stream stream) { throw null; }
protected virtual void SaveCore(System.IO.Stream stream) { }
public void SetCustomAttribute(System.Reflection.ConstructorInfo con, byte[] binaryAttribute) { }
public void SetCustomAttribute(System.Reflection.Emit.CustomAttributeBuilder customBuilder) { }
protected abstract void SetCustomAttributeCore(System.Reflection.ConstructorInfo con, System.ReadOnlySpan<byte> binaryAttribute);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
<linker>
<assembly fullname="System.Reflection.Emit">
<type fullname="System.Reflection.Emit.AssemblyBuilderImpl">
<!-- Internal API used by tests only. -->
<method name="DefinePersistedAssembly" />
<method name="Save" />
<!-- Internal API called through Reflection by another assembly. -->
<method name=".ctor" />
</type>
</assembly>
</linker>
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,9 @@ internal sealed class AssemblyBuilderImpl : AssemblyBuilder

internal List<CustomAttributeWrapper>? _customAttributes;

internal AssemblyBuilderImpl(AssemblyName name, Assembly coreAssembly, IEnumerable<CustomAttributeBuilder>? assemblyAttributes)
internal AssemblyBuilderImpl(AssemblyName name, Assembly coreAssembly, IEnumerable<CustomAttributeBuilder>? assemblyAttributes = null)
{
ArgumentNullException.ThrowIfNull(name);

name = (AssemblyName)name.Clone();

ArgumentException.ThrowIfNullOrEmpty(name.Name, "AssemblyName.Name");

_assemblyName = name;
_assemblyName = (AssemblyName)name.Clone();
_coreAssembly = coreAssembly;
_metadataBuilder = new MetadataBuilder();

Expand All @@ -40,10 +34,6 @@ internal AssemblyBuilderImpl(AssemblyName name, Assembly coreAssembly, IEnumerab
}
}

internal static AssemblyBuilderImpl DefinePersistedAssembly(AssemblyName name, Assembly coreAssembly,
IEnumerable<CustomAttributeBuilder>? assemblyAttributes)
=> new AssemblyBuilderImpl(name, coreAssembly, assemblyAttributes);

private void WritePEImage(Stream peStream, BlobBuilder ilBuilder, BlobBuilder fieldData)
{
var peHeaderBuilder = new PEHeaderBuilder(
Expand All @@ -64,7 +54,7 @@ private void WritePEImage(Stream peStream, BlobBuilder ilBuilder, BlobBuilder fi
peBlob.WriteContentTo(peStream);
}

internal void Save(Stream stream)
protected override void SaveCore(Stream stream)
{
ArgumentNullException.ThrowIfNull(stream);

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

internal void Save(string assemblyFileName)
{
ArgumentNullException.ThrowIfNull(assemblyFileName);

using var peStream = new FileStream(assemblyFileName, FileMode.Create, FileAccess.Write);
Save(peStream);
}

protected override ModuleBuilder DefineDynamicModuleCore(string name)
{
if (_module != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public void AssemblyWithDifferentTypes()
aName.CultureInfo = new CultureInfo("en");
aName.Flags = AssemblyNameFlags.Retargetable;

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

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

Expand Down Expand Up @@ -213,10 +213,12 @@ public void AssemblyWithDifferentTypes()
eventb.SetRemoveOnMethod(mbRemove);
tbEvents.CreateType();

saveMethod.Invoke(ab, [file.Path]);
ab.Save(file.Path);

Assembly assemblyFromDisk = AssemblySaveTools.LoadAssemblyFromPath(file.Path);
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
CheckAssembly(assemblyFromDisk);
using (MetadataLoadContext mlc = new MetadataLoadContext(new CoreMetadataAssemblyResolver()))
{
CheckAssembly(mlc.LoadFromAssemblyPath(file.Path));
}
}
}

Expand Down
Loading
Loading