| File: System\Reflection\Emit\PersistedAssemblyBuilder.cs | Web Access |
| Project: src\runtime\src\libraries\System.Reflection.Emit\src\System.Reflection.Emit.csproj (System.Reflection.Emit) |
// 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 { /// <summary> /// AssemblyBuilder implementation that can persist assembly to a disk or stream. /// </summary> public sealed class PersistedAssemblyBuilder : AssemblyBuilder { private readonly AssemblyName _assemblyName; private readonly Assembly _coreAssembly; private readonly MetadataBuilder _metadataBuilder; private ModuleBuilderImpl? _module; private bool _isMetadataPopulated; internal List<CustomAttributeWrapper>? _customAttributes; /// <summary> /// Creates a <see cref="PersistedAssemblyBuilder"/> instance 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="PersistedAssemblyBuilder"/> 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 PersistedAssemblyBuilder(AssemblyName name, Assembly coreAssembly, IEnumerable<CustomAttributeBuilder>? assemblyAttributes = null) { ArgumentNullException.ThrowIfNull(name); ArgumentException.ThrowIfNullOrEmpty(name.Name, "AssemblyName.Name"); ArgumentNullException.ThrowIfNull(coreAssembly); _assemblyName = (AssemblyName)name.Clone(); _coreAssembly = coreAssembly; _metadataBuilder = new MetadataBuilder(); if (assemblyAttributes != null) { foreach (CustomAttributeBuilder assemblyAttribute in assemblyAttributes) { SetCustomAttribute(assemblyAttribute); } } } private void WritePEImage(Stream peStream, BlobBuilder ilBuilder, BlobBuilder fieldData) { var peHeaderBuilder = new PEHeaderBuilder( // For now only support DLL, DLL files are considered executable files // for almost all purposes, although they cannot be directly run. imageCharacteristics: Characteristics.ExecutableImage | Characteristics.Dll); var peBuilder = new ManagedPEBuilder( header: peHeaderBuilder, metadataRootBuilder: new MetadataRootBuilder(_metadataBuilder), ilStream: ilBuilder, mappedFieldData: fieldData, strongNameSignatureSize: 0); // Write executable into the specified stream. var peBlob = new BlobBuilder(); peBuilder.Serialize(peBlob); peBlob.WriteContentTo(peStream); } /// <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="InvalidOperationException">A module not defined for the assembly.</exception> /// <exception cref="InvalidOperationException">The metadata already populated for the assembly before.</exception> public void Save(Stream stream) => SaveInternal(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="InvalidOperationException">A module not defined for the assembly.</exception> /// <exception cref="InvalidOperationException">The metadata already populated for the assembly before.</exception> public void Save(string assemblyFileName) { ArgumentNullException.ThrowIfNull(assemblyFileName); using var peStream = new FileStream(assemblyFileName, FileMode.Create, FileAccess.Write); SaveInternal(peStream); } private void SaveInternal(Stream stream) { ArgumentNullException.ThrowIfNull(stream); PopulateAssemblyMetadata(out BlobBuilder ilStream, out BlobBuilder fieldData, out _); WritePEImage(stream, ilStream, fieldData); } /// <summary> /// Generates the metadata for the <see cref="PersistedAssemblyBuilder"/>. /// </summary> /// <param name="ilStream">Outputs <see cref="BlobBuilder"/> bytes that includes all method's IL (body) emitted.</param> /// <param name="mappedFieldData">Outputs <see cref="BlobBuilder"/> bytes that includes all field RVA data defined in the assembly.</param> /// <returns>A <see cref="MetadataBuilder"/> that includes all members defined in the Assembly.</returns> /// <exception cref="InvalidOperationException">A module not defined for the assembly.</exception> /// <exception cref="InvalidOperationException">The metadata already populated for the assembly previously.</exception> [CLSCompliant(false)] public MetadataBuilder GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder mappedFieldData) { PopulateAssemblyMetadata(out ilStream, out mappedFieldData, out _); return _metadataBuilder; } /// <summary> /// Generates the metadata for the <see cref="PersistedAssemblyBuilder"/>. /// </summary> /// <param name="ilStream">Outputs <see cref="BlobBuilder"/> bytes that includes all method's IL (body) emitted.</param> /// <param name="mappedFieldData">Outputs <see cref="BlobBuilder"/> bytes that includes all field RVA data defined in the assembly.</param> /// <param name="pdbBuilder">Outputs <see cref="MetadataBuilder"/> that includes PDB metadata.</param> /// <returns>A <see cref="MetadataBuilder"/> that includes all members defined in the Assembly.</returns> /// <exception cref="InvalidOperationException">A module not defined for the assembly.</exception> /// <exception cref="InvalidOperationException">The metadata already populated for the assembly previously.</exception> [CLSCompliant(false)] public MetadataBuilder GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder mappedFieldData, out MetadataBuilder pdbBuilder) { PopulateAssemblyMetadata(out ilStream, out mappedFieldData, out pdbBuilder); return _metadataBuilder; } private void PopulateAssemblyMetadata(out BlobBuilder ilStream, out BlobBuilder fieldData, out MetadataBuilder pdbBuilder) { if (_module == null) { throw new InvalidOperationException(SR.InvalidOperation_AModuleRequired); } if (_isMetadataPopulated) // Cannot populate assembly metadata multiple times. This is consistent with Save() in .Net Framework. { throw new InvalidOperationException(SR.InvalidOperation_CannotPopulateMultipleTimes); } ilStream = new BlobBuilder(); fieldData = new BlobBuilder(); // Add assembly metadata AssemblyDefinitionHandle assemblyHandle = _metadataBuilder.AddAssembly( _metadataBuilder.GetOrAddString(value: _assemblyName.Name!), version: _assemblyName.Version ?? new Version(0, 0, 0, 0), culture: _assemblyName.CultureName == null ? default : _metadataBuilder.GetOrAddString(value: _assemblyName.CultureName), publicKey: _assemblyName.GetPublicKey() is byte[] publicKey ? _metadataBuilder.GetOrAddBlob(value: publicKey) : default, flags: AddContentType((AssemblyFlags)_assemblyName.Flags, _assemblyName.ContentType), #pragma warning disable SYSLIB0037 // Type or member is obsolete hashAlgorithm: (AssemblyHashAlgorithm)_assemblyName.HashAlgorithm #pragma warning restore SYSLIB0037 ); _module.WriteCustomAttributes(_customAttributes, assemblyHandle); _module.AppendMetadata(new MethodBodyStreamEncoder(ilStream), fieldData, out pdbBuilder); _isMetadataPopulated = true; } private static AssemblyFlags AddContentType(AssemblyFlags flags, AssemblyContentType contentType) => (AssemblyFlags)((int)contentType << 9) | flags; protected override ModuleBuilder DefineDynamicModuleCore(string name) { if (_module != null) { throw new InvalidOperationException(SR.InvalidOperation_NoMultiModuleAssembly); } _module = new ModuleBuilderImpl(name, _coreAssembly, _metadataBuilder, this); return _module; } protected override ModuleBuilder? GetDynamicModuleCore(string name) { if (_module != null && _module.ScopeName.Equals(name)) { return _module; } return null; } protected override void SetCustomAttributeCore(ConstructorInfo con, ReadOnlySpan<byte> binaryAttribute) { _customAttributes ??= new List<CustomAttributeWrapper>(); _customAttributes.Add(new CustomAttributeWrapper(con, binaryAttribute)); } public override string? FullName => _assemblyName.FullName; public override Module ManifestModule => _module ?? throw new InvalidOperationException(SR.InvalidOperation_AModuleRequired); public override AssemblyName GetName(bool copiedName) => (AssemblyName)_assemblyName.Clone(); } }