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

Initial step for adding AssemblyBuilder.Save implementation #83554

Merged
merged 37 commits into from
Apr 1, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
247cc65
Initial import AB.Save
buyaa-n Feb 13, 2023
fefba4f
Move code from S.R.Metadata to S.R.Emit
steveharter Mar 2, 2023
063a774
Merge branch 'main' of https://github.com/dotnet/runtime into ab-save
buyaa-n Mar 2, 2023
c4da1d6
Add additional proj refs for VS
steveharter Mar 6, 2023
f0056ac
Merge branch 'ab-save' of github.com:buyaa-n/runtime into ab-save
buyaa-n Mar 6, 2023
625539e
Some refactoring, handle CustomAttributes for Assembly/Module
buyaa-n Mar 14, 2023
7df8da2
Save custom attributes for method/fields
buyaa-n Mar 15, 2023
feb65a4
Remove public APIs until API shape approved
buyaa-n Mar 16, 2023
d78c7d0
Remove custom attibutes related changes
buyaa-n Mar 16, 2023
22f8bb1
Apply feedbacks
buyaa-n Mar 17, 2023
e78d118
Remove CodeDom reference
buyaa-n Mar 18, 2023
9c535d9
Add resources strings
buyaa-n Mar 20, 2023
05fbd7a
Update src/libraries/System.Reflection.Emit/src/Resources/Strings.resx
danmoseley Mar 20, 2023
e7a19e8
Fix field token issue
buyaa-n Mar 20, 2023
689418e
Merge branch 'ab-save' of github.com:buyaa-n/runtime into ab-save
buyaa-n Mar 20, 2023
6ee694c
Remove parameter testing
buyaa-n Mar 21, 2023
fbb0477
Remove unneeded SignatureHelper call
buyaa-n Mar 21, 2023
4dc0be1
Namespace, name updates
buyaa-n Mar 22, 2023
c6b65c9
Update names
buyaa-n Mar 22, 2023
98d07e6
Apply suggestions from code review
buyaa-n Mar 24, 2023
3570054
Apply more feedback
buyaa-n Mar 24, 2023
14f1141
Create the module by name and other feedbacks addressed
buyaa-n Mar 24, 2023
a5ef839
Apply feedbacks
buyaa-n Mar 25, 2023
849d929
Move validations from Runtime *Builders
buyaa-n Mar 26, 2023
c4726ea
Apply more feedback
buyaa-n Mar 27, 2023
0093baa
Update some comments
buyaa-n Mar 28, 2023
9630299
Update core assembly type handling
buyaa-n Mar 29, 2023
b84d99e
Cache the core primitives
buyaa-n Mar 29, 2023
0327112
Update src/libraries/System.Reflection.Emit/src/System/Reflection/Emi…
buyaa-n Mar 29, 2023
8a1bc21
Merge branch 'main' of https://github.com/dotnet/runtime into ab-save
buyaa-n Mar 30, 2023
b5c1df9
Apply more feedback
buyaa-n Mar 30, 2023
eda5e11
Move wrong validation
buyaa-n Mar 30, 2023
60c9ad5
Apply more feedbacks
buyaa-n Mar 31, 2023
8f3be48
Update src/libraries/System.Reflection.Emit/src/System/Reflection/Emi…
buyaa-n Mar 31, 2023
014e1fa
Merge some of the test comparisons
buyaa-n Mar 31, 2023
3e3b854
Refactor tests
buyaa-n Mar 31, 2023
e4aa8b1
Update test with meaningful name and other small changes
buyaa-n Mar 31, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
304 changes: 250 additions & 54 deletions src/libraries/System.Reflection.Emit/System.Reflection.Emit.sln

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:System.Reflection.Emit.Experiment.PersistableAssemblyBuilder</Target>
<Left>ref/net8.0/System.Reflection.Emit.dll</Left>
<Right>lib/net8.0/System.Reflection.Emit.dll</Right>
</Suppression>
</Suppressions>
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsPartialFacadeAssembly>true</IsPartialFacadeAssembly>
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
<IsPartialFacadeAssembly>true</IsPartialFacadeAssembly>
<ContractTypesPartiallyMoved>true</ContractTypesPartiallyMoved>
</PropertyGroup>
<ItemGroup>
<Compile Include="System\Reflection\Emit\MetadataHelper.cs" />
<Compile Include="System\Reflection\Emit\PersistableAssemblyBuilder.cs" />
<Compile Include="System\Reflection\Emit\PersistableFieldBuilder.cs" />
<Compile Include="System\Reflection\Emit\PersistableMethodBuilder.cs" />
<Compile Include="System\Reflection\Emit\PersistableModuleBuilder.cs" />
<Compile Include="System\Reflection\Emit\PersistableTypeBuilder.cs" />
<Compile Include="System\Reflection\Emit\SignatureHelper.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(CoreLibProject)" />
<ProjectReference Include="$(LibrariesProjectRoot)\System.Collections.Immutable\src\System.Collections.Immutable.csproj" />
<ProjectReference Include="$(LibrariesProjectRoot)\System.Reflection.Metadata\src\System.Reflection.Metadata.csproj" />
<ProjectReference Include="$(LibrariesProjectRoot)\System.Runtime\src\System.Runtime.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;

namespace System.Reflection.Emit.Experiment
{
//This static helper class adds common entities to a Metadata Builder.
internal static class MetadataHelper
{
internal static AssemblyReferenceHandle AddAssemblyReference(Assembly assembly, MetadataBuilder metadata)
{
AssemblyName assemblyName = assembly.GetName();

if (assemblyName == null || assemblyName.Name == null)
{
throw new ArgumentNullException(nameof(assemblyName));
}

buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
return AddAssemblyReference(metadata, assemblyName.Name, assemblyName.Version, assemblyName.CultureName, assemblyName.GetPublicKey(), (AssemblyFlags)assemblyName.Flags);
}

internal static AssemblyReferenceHandle AddAssemblyReference(MetadataBuilder metadata, string name, Version? version, string? culture, byte[]? publicKey, AssemblyFlags flags)
{
return metadata.AddAssemblyReference(
name: metadata.GetOrAddString(name),
version: version ?? new Version(0, 0, 0, 0),
culture: (culture == null) ? default : metadata.GetOrAddString(value: culture),
publicKeyOrToken: (publicKey == null) ? default : metadata.GetOrAddBlob(publicKey),
flags: flags,
hashValue: default); // not sure where to find hashValue.
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
}

internal static TypeDefinitionHandle AddTypeDef(MetadataBuilder metadata, PersistableTypeBuilder typeBuilder, EntityHandle baseType, int methodToken)
{
//Add type metadata
return metadata.AddTypeDefinition(
attributes: typeBuilder.Attributes,
(typeBuilder.Namespace == null) ? default : metadata.GetOrAddString(typeBuilder.Namespace),
name: metadata.GetOrAddString(typeBuilder.Name),
baseType: baseType,
fieldList: MetadataTokens.FieldDefinitionHandle(1), //Update once we support fields.
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
methodList: MetadataTokens.MethodDefinitionHandle(methodToken));
}

internal static TypeReferenceHandle AddTypeReference(MetadataBuilder metadata, Type type, AssemblyReferenceHandle parent)
{
return AddTypeReference(metadata, parent, type.Name, type.Namespace);
}

internal static TypeReferenceHandle AddTypeReference(MetadataBuilder metadata, AssemblyReferenceHandle parent, string name, string? nameSpace)
{
return metadata.AddTypeReference(
parent,
(nameSpace == null) ? default : metadata.GetOrAddString(nameSpace),
metadata.GetOrAddString(name)
);
}

internal static MethodDefinitionHandle AddMethodDefintion(MetadataBuilder metadata, PersistableMethodBuilder methodBuilder)
{
return metadata.AddMethodDefinition(
methodBuilder.Attributes,
MethodImplAttributes.IL,
metadata.GetOrAddString(methodBuilder.Name),
metadata.GetOrAddBlob(MetadataSignatureHelper.MethodSignatureEncoder(methodBuilder._parameters, methodBuilder._returnType, !methodBuilder.IsStatic)),
-1, //No body supported
parameterList: default
);
}

internal static FieldDefinitionHandle AddFieldDefintion(MetadataBuilder metadata, FieldInfo field)
{
return metadata.AddFieldDefinition(field.Attributes, metadata.GetOrAddString(field.Name),
metadata.GetOrAddBlob(MetadataSignatureHelper.FieldSignatureEncoder(field.FieldType)));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// 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.IO;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;

namespace System.Reflection.Emit.Experiment
{
public sealed class PersistableAssemblyBuilder : AssemblyBuilder
{
private bool _previouslySaved;
private AssemblyName _assemblyName;
private PersistableModuleBuilder? _module;

internal PersistableAssemblyBuilder(AssemblyName name, IEnumerable<CustomAttributeBuilder>? assemblyAttributes)
{
ArgumentNullException.ThrowIfNull(name);

_assemblyName = name;

_module = new PersistableModuleBuilder(PersistableModuleBuilder.ManifestModuleName, this);

if (assemblyAttributes != null)
{
foreach (CustomAttributeBuilder assemblyAttribute in assemblyAttributes)
{
SetCustomAttribute(assemblyAttribute);
}
}
}

public static PersistableAssemblyBuilder DefineDynamicAssembly(AssemblyName name)
=> new PersistableAssemblyBuilder(name, null);

public static PersistableAssemblyBuilder DefineDynamicAssembly(
AssemblyName name,
IEnumerable<CustomAttributeBuilder>? assemblyAttributes)
=> new PersistableAssemblyBuilder(name, assemblyAttributes);

private static void WritePEImage(Stream peStream, MetadataBuilder metadataBuilder, BlobBuilder ilBuilder) // MethodDefinitionHandle entryPointHandle when we have main method.
{
// Create executable with the managed metadata from the specified MetadataBuilder.
var peHeaderBuilder = new PEHeaderBuilder(
imageCharacteristics: Characteristics.Dll // Start off with a simple DLL
);

var peBuilder = new ManagedPEBuilder(
peHeaderBuilder,
new MetadataRootBuilder(metadataBuilder),
ilBuilder,
flags: CorFlags.ILOnly,
deterministicIdProvider: content => new BlobContentId(Guid.NewGuid(), 0x04030201)); // Const ID, will reexamine as project progresses.

// Write executable into the specified stream.
var peBlob = new BlobBuilder();
BlobContentId contentId = peBuilder.Serialize(peBlob);
peBlob.WriteContentTo(peStream);
}

public void Save(Stream stream)
{
if (_previouslySaved) // Cannot save an assembly multiple times. This is consistent with Save() in .Net Framework.
{
throw new InvalidOperationException("Cannot save an assembly multiple times");
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
}

ArgumentNullException.ThrowIfNull(stream);

if (_assemblyName == null || _assemblyName.Name == null)
{
throw new InvalidOperationException();
}

if (_module == null)
{
throw new InvalidOperationException("Assembly needs at least one module defined");
}

// Add assembly metadata
var metadata = new MetadataBuilder();
metadata.AddAssembly( // Metadata is added for the new assembly - Current design - metadata generated only when Save method is called.
metadata.GetOrAddString(value: _assemblyName.Name),
version: _assemblyName.Version ?? new Version(0, 0, 0, 0),
culture: _assemblyName.CultureName == null ? default : metadata.GetOrAddString(value: _assemblyName.CultureName),
publicKey: _assemblyName.GetPublicKey() is byte[] publicKey ? metadata.GetOrAddBlob(value: publicKey) : default,
flags: (AssemblyFlags)_assemblyName.Flags,
hashAlgorithm: AssemblyHashAlgorithm.None); // AssemblyName.HashAlgorithm is obsolete so default value used.

// Add module's metadata
_module.AppendMetadata(metadata);

var ilBuilder = new BlobBuilder();
WritePEImage(stream, metadata, ilBuilder);
_previouslySaved = true;
}

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

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

protected override ModuleBuilder DefineDynamicModuleCore(string name)
{
ArgumentException.ThrowIfNullOrEmpty(name);

if (_module == null)
{
throw new InvalidOperationException("Module not found");
}

return _module;
}

protected override ModuleBuilder? GetDynamicModuleCore(string name)
{
ArgumentException.ThrowIfNullOrEmpty(name);

if (PersistableModuleBuilder.ManifestModuleName.Equals(name))
{
return _module;
}

return null;
}

protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) => throw new NotSupportedException();

protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) => throw new NotSupportedException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// 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.Globalization;

namespace System.Reflection.Emit.Experiment
{
internal sealed class PersistableFieldBuilder : FieldBuilder
{
private PersistableTypeBuilder _typeBuilder;
private string _fieldName;
private FieldAttributes _attributes;
private Type _fieldType;

internal PersistableFieldBuilder(PersistableTypeBuilder typeBuilder, string fieldName, Type type,
Type[]? requiredCustomModifiers, Type[]? optionalCustomModifiers, FieldAttributes attributes)
{
ArgumentException.ThrowIfNullOrEmpty(fieldName);

ArgumentNullException.ThrowIfNull(type);

if (type == typeof(void))
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
throw new ArgumentException("Bad field type");

_fieldName = fieldName;

_typeBuilder = typeBuilder;
_fieldType = type;
_attributes = attributes & ~FieldAttributes.ReservedMask;

SignatureHelper sigHelp = SignatureHelper.GetFieldSigHelper(_typeBuilder.Module);
sigHelp.AddArgument(type, requiredCustomModifiers, optionalCustomModifiers);
}

#region MemberInfo Overrides
protected override void SetConstantCore(object? defaultValue) => throw new NotImplementedException();
protected override void SetCustomAttributeCore(ConstructorInfo con, byte[] binaryAttribute) => throw new NotImplementedException();

protected override void SetCustomAttributeCore(CustomAttributeBuilder customBuilder) => throw new NotImplementedException();
protected override void SetOffsetCore(int iOffset) => throw new NotImplementedException();

public override int MetadataToken => throw new NotImplementedException();

public override Module Module => _typeBuilder.Module;

public override string Name => _fieldName;

public override Type? DeclaringType
{
get
{
return _typeBuilder;
buyaa-n marked this conversation as resolved.
Show resolved Hide resolved
}
}

public override Type? ReflectedType
{
get
{
return _typeBuilder;
}
}

#endregion

#region FieldInfo Overrides
public override Type FieldType => _fieldType;

public override object? GetValue(object? obj) => throw new NotSupportedException();

public override void SetValue(object? obj, object? val, BindingFlags invokeAttr, Binder? binder, CultureInfo? culture) => throw new NotSupportedException();
public override RuntimeFieldHandle FieldHandle => throw new NotSupportedException();

public override FieldAttributes Attributes => _attributes;

#endregion

#region ICustomAttributeProvider Implementation
public override object[] GetCustomAttributes(bool inherit) => throw new NotSupportedException();

public override object[] GetCustomAttributes(Type attributeType, bool inherit) => throw new NotSupportedException();

public override bool IsDefined(Type attributeType, bool inherit) => throw new NotSupportedException();
#endregion
}
}
Loading