|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Reflection.PortableExecutable;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis
{
/// <summary>
/// Represents metadata image reference.
/// </summary>
/// <remarks>
/// Represents a logical location of the image, not the content of the image.
/// The content might change in time. A snapshot is taken when the compiler queries the reference for its metadata.
/// </remarks>
public abstract class MetadataReference
{
public MetadataReferenceProperties Properties { get; }
protected MetadataReference(MetadataReferenceProperties properties)
{
this.Properties = properties;
}
/// <summary>
/// Path or name used in error messages to identity the reference.
/// </summary>
public virtual string? Display { get { return null; } }
/// <summary>
/// Returns true if this reference is an unresolved reference.
/// </summary>
internal virtual bool IsUnresolved
{
get { return false; }
}
/// <summary>
/// Returns an instance of the reference with specified aliases.
/// </summary>
/// <param name="aliases">The new aliases for the reference.</param>
/// <exception cref="ArgumentException">Alias is invalid for the metadata kind.</exception>
public MetadataReference WithAliases(IEnumerable<string> aliases)
{
return WithAliases(ImmutableArray.CreateRange(aliases));
}
/// <summary>
/// Returns an instance of the reference with specified interop types embedding.
/// </summary>
/// <param name="value">The new value for <see cref="MetadataReferenceProperties.EmbedInteropTypes"/>.</param>
/// <exception cref="ArgumentException">Interop types can't be embedded from modules.</exception>
public MetadataReference WithEmbedInteropTypes(bool value)
{
return WithProperties(Properties.WithEmbedInteropTypes(value));
}
/// <summary>
/// Returns an instance of the reference with specified aliases.
/// </summary>
/// <param name="aliases">The new aliases for the reference.</param>
/// <exception cref="ArgumentException">Alias is invalid for the metadata kind.</exception>
public MetadataReference WithAliases(ImmutableArray<string> aliases)
{
return WithProperties(Properties.WithAliases(aliases));
}
/// <summary>
/// Returns an instance of the reference with specified properties, or this instance if properties haven't changed.
/// </summary>
/// <param name="properties">The new properties for the reference.</param>
/// <exception cref="ArgumentException">Specified values not valid for this reference.</exception>
public MetadataReference WithProperties(MetadataReferenceProperties properties)
{
if (properties == this.Properties)
{
return this;
}
return WithPropertiesImplReturningMetadataReference(properties);
}
internal abstract MetadataReference WithPropertiesImplReturningMetadataReference(MetadataReferenceProperties properties);
/// <summary>
/// Creates a reference to a single-module assembly or a standalone module stored in memory.
/// </summary>
/// <param name="peImage">Assembly image.</param>
/// <param name="properties">Reference properties (extern aliases, type embedding, <see cref="MetadataImageKind"/>).</param>
/// <param name="documentation">Provides XML documentation for symbol found in the reference.</param>
/// <param name="filePath">Optional path that describes the location of the metadata. The file doesn't need to exist on disk. The path is opaque to the compiler.</param>
/// <remarks>
/// Performance considerations:
/// <para>
/// It is recommended to use <see cref="AssemblyMetadata.CreateFromImage(ImmutableArray{byte})"/> or <see cref="ModuleMetadata.CreateFromImage(ImmutableArray{byte})"/>
/// API when creating multiple references to the same metadata.
/// Reusing <see cref="Metadata"/> object to create multiple references allows for sharing data across these references.
/// </para>
/// <para>
/// The method pins <paramref name="peImage"/> in managed heap. The pinned memory is released
/// when the resulting reference becomes unreachable and GC collects it. To control the lifetime of the pinned memory
/// deterministically use <see cref="AssemblyMetadata.CreateFromImage(ImmutableArray{byte})"/>
/// to create an <see cref="IDisposable"/> metadata object and
/// <see cref="AssemblyMetadata.GetReference(DocumentationProvider, ImmutableArray{string}, bool, string, string)"/> to get a reference to it.
/// </para>
/// <para>
/// The method creates a reference to a single-module assembly. To create a reference to a multi-module assembly or a stand-alone module use
/// <see cref="ModuleMetadata.CreateFromImage(ImmutableArray{byte})"/> and <see cref="ModuleMetadata.GetReference(DocumentationProvider, string, string)"/>.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="peImage"/> is null.</exception>
public static PortableExecutableReference CreateFromImage(
ImmutableArray<byte> peImage,
MetadataReferenceProperties properties = default,
DocumentationProvider? documentation = null,
string? filePath = null)
{
Metadata metadata = properties.Kind switch
{
MetadataImageKind.Module => ModuleMetadata.CreateFromImage(peImage),
_ => AssemblyMetadata.CreateFromImage(peImage),
};
return new MetadataImageReference(metadata, properties, documentation, filePath, display: null);
}
/// <summary>
/// Creates a reference to a single-module assembly or a standalone module stored in memory.
/// </summary>
/// <param name="peImage">Assembly image.</param>
/// <param name="properties">Reference properties (extern aliases, type embedding, <see cref="MetadataImageKind"/>).</param>
/// <param name="documentation">Provides XML documentation for symbol found in the reference.</param>
/// <param name="filePath">Optional path that describes the location of the metadata. The file doesn't need to exist on disk. The path is opaque to the compiler.</param>
/// <remarks>
/// Performance considerations:
/// <para>
/// It is recommended to use <see cref="AssemblyMetadata.CreateFromImage(IEnumerable{byte})"/> or <see cref="ModuleMetadata.CreateFromImage(IEnumerable{byte})"/>
/// API when creating multiple references to the same metadata.
/// Reusing <see cref="Metadata"/> object to create multiple references allows for sharing data across these references.
/// </para>
/// <para>
/// The method makes a copy of the data and pins it. To avoid making a copy use an overload that takes an <see cref="ImmutableArray{T}"/>.
/// The pinned memory is released when the resulting reference becomes unreachable and GC collects it. To control the lifetime of the pinned memory
/// deterministically use <see cref="AssemblyMetadata.CreateFromStream(Stream, PEStreamOptions)"/>
/// to create an <see cref="IDisposable"/> metadata object and
/// <see cref="AssemblyMetadata.GetReference(DocumentationProvider, ImmutableArray{string}, bool, string, string)"/> to get a reference to it.
/// </para>
/// </remarks>
/// <exception cref="ArgumentNullException"><paramref name="peImage"/> is null.</exception>
public static PortableExecutableReference CreateFromImage(
IEnumerable<byte> peImage,
MetadataReferenceProperties properties = default,
DocumentationProvider? documentation = null,
string? filePath = null)
{
Metadata metadata = properties.Kind switch
{
MetadataImageKind.Module => ModuleMetadata.CreateFromImage(peImage),
_ => AssemblyMetadata.CreateFromImage(peImage),
};
return new MetadataImageReference(metadata, properties, documentation, filePath, display: null);
}
/// <summary>
/// Creates a reference to a single-module assembly or a stand-alone module from data in specified stream.
/// Reads the content of the stream into memory and closes the stream upon return.
/// </summary>
/// <param name="peStream">Assembly image.</param>
/// <param name="properties">Reference properties (extern aliases, type embedding, <see cref="MetadataImageKind"/>).</param>
/// <param name="documentation">Provides XML documentation for symbol found in the reference.</param>
/// <param name="filePath">Optional path that describes the location of the metadata. The file doesn't need to exist on disk. The path is opaque to the compiler.</param>
/// <exception cref="ArgumentException"><paramref name="peStream"/> doesn't support read and seek operations.</exception>
/// <exception cref="ArgumentNullException"><paramref name="peStream"/> is null.</exception>
/// <exception cref="IOException">An error occurred while reading the stream.</exception>
/// <remarks>
/// Performance considerations:
/// <para>
/// It is recommended to use <see cref="AssemblyMetadata.CreateFromStream(Stream, PEStreamOptions)"/> or <see cref="ModuleMetadata.CreateFromStream(Stream, PEStreamOptions)"/>
/// API when creating multiple references to the same metadata.
/// Reusing <see cref="Metadata"/> object to create multiple references allows for sharing data across these references.
/// </para>
/// <para>
/// The method eagerly reads the entire content of <paramref name="peStream"/> into native heap. The native memory block is released
/// when the resulting reference becomes unreachable and GC collects it. To decrease memory footprint of the reference and/or manage
/// the lifetime deterministically use <see cref="AssemblyMetadata.CreateFromStream(Stream, PEStreamOptions)"/>
/// to create an <see cref="IDisposable"/> metadata object and
/// <see cref="AssemblyMetadata.GetReference(DocumentationProvider, ImmutableArray{string}, bool, string, string)"/>
/// to get a reference to it.
/// </para>
/// </remarks>
public static PortableExecutableReference CreateFromStream(
Stream peStream,
MetadataReferenceProperties properties = default,
DocumentationProvider? documentation = null,
string? filePath = null)
{
// Prefetch data and close the stream.
Metadata metadata = properties.Kind switch
{
MetadataImageKind.Module => ModuleMetadata.CreateFromStream(peStream, PEStreamOptions.PrefetchEntireImage),
_ => AssemblyMetadata.CreateFromStream(peStream, PEStreamOptions.PrefetchEntireImage),
};
return new MetadataImageReference(metadata, properties, documentation, filePath, display: null);
}
/// <summary>
/// Creates a reference to an assembly or standalone module stored in a file.
/// Reads the content of the file into memory.
/// </summary>
/// <param name="path">Path to the assembly file.</param>
/// <param name="properties">Reference properties (extern aliases, type embedding, <see cref="MetadataImageKind"/>).</param>
/// <param name="documentation">Provides XML documentation for symbol found in the reference.</param>
/// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="path"/> is invalid.</exception>
/// <exception cref="IOException">An error occurred while reading the file.</exception>
/// <remarks>
/// Performance considerations:
/// <para>
/// It is recommended to use <see cref="AssemblyMetadata.CreateFromFile(string)"/> or <see cref="ModuleMetadata.CreateFromFile(string)"/>
/// API when creating multiple references to the same file.
/// Reusing <see cref="Metadata"/> object allows for sharing data across these references.
/// </para>
/// <para>
/// The method eagerly reads the entire content of the file into native heap. The native memory block is released
/// when the resulting reference becomes unreachable and GC collects it. To decrease memory footprint of the reference and/or manage
/// the lifetime deterministically use <see cref="AssemblyMetadata.CreateFromFile(string)"/>
/// to create an <see cref="IDisposable"/> metadata object and
/// <see cref="AssemblyMetadata.GetReference(DocumentationProvider, ImmutableArray{string}, bool, string, string)"/>
/// to get a reference to it.
/// </para>
/// </remarks>
public static PortableExecutableReference CreateFromFile(
string path,
MetadataReferenceProperties properties = default,
DocumentationProvider? documentation = null) =>
CreateFromFile(
StandardFileSystem.Instance.OpenFileWithNormalizedException(path, FileMode.Open, FileAccess.Read, FileShare.Read),
path,
PEStreamOptions.PrefetchEntireImage,
properties,
documentation);
internal static MetadataImageReference CreateFromFile(
string path,
PEStreamOptions options,
MetadataReferenceProperties properties,
DocumentationProvider? documentation = null) =>
CreateFromFile(
StandardFileSystem.Instance.OpenFileWithNormalizedException(path, FileMode.Open, FileAccess.Read, FileShare.Read),
path,
options,
properties,
documentation);
internal static MetadataImageReference CreateFromFile(
Stream peStream,
string path,
PEStreamOptions options,
MetadataReferenceProperties properties,
DocumentationProvider? documentation = null)
{
// prefetch image, close stream to avoid locking it:
var module = ModuleMetadata.CreateFromStream(peStream, options);
if (properties.Kind == MetadataImageKind.Module)
{
return new MetadataImageReference(module, properties, documentation, path, display: null);
}
// any additional modules constituting the assembly will be read lazily:
var assemblyMetadata = AssemblyMetadata.CreateFromFile(module, path);
return new MetadataImageReference(assemblyMetadata, properties, documentation, path, display: null);
}
/// <summary>
/// Creates a reference to a loaded assembly.
/// </summary>
/// <param name="assembly">Path to the module file.</param>
/// <exception cref="ArgumentNullException"><paramref name="assembly"/> is null.</exception>
/// <exception cref="NotSupportedException"><paramref name="assembly"/> is dynamic, doesn't have a location, or the platform doesn't support reading from the location.</exception>
/// <remarks>
/// Performance considerations:
/// <para>
/// It is recommended to use <see cref="AssemblyMetadata.CreateFromFile(string)"/> API when creating multiple references to the same assembly.
/// Reusing <see cref="AssemblyMetadata"/> object allows for sharing data across these references.
/// </para>
/// </remarks>
[Obsolete("Use CreateFromFile(assembly.Location) instead", error: true)]
[EditorBrowsable(EditorBrowsableState.Never)]
public static MetadataReference CreateFromAssembly(Assembly assembly)
{
return CreateFromAssemblyInternal(assembly);
}
internal static MetadataImageReference CreateFromAssemblyInternal(Assembly assembly)
{
return CreateFromAssemblyInternal(assembly, default(MetadataReferenceProperties));
}
/// <summary>
/// Creates a reference to a loaded assembly.
/// </summary>
/// <param name="assembly">Path to the module file.</param>
/// <param name="properties">Reference properties (extern aliases, type embedding).</param>
/// <param name="documentation">Provides XML documentation for symbol found in the reference.</param>
/// <exception cref="ArgumentNullException"><paramref name="assembly"/> is null.</exception>
/// <exception cref="ArgumentException"><paramref name="properties"/>.<see cref="MetadataReferenceProperties.Kind"/> is not <see cref="MetadataImageKind.Assembly"/>.</exception>
/// <exception cref="NotSupportedException"><paramref name="assembly"/> is dynamic, doesn't have a location, or the platform doesn't support reading from the location.</exception>
/// <remarks>
/// Performance considerations:
/// <para>
/// It is recommended to use <see cref="AssemblyMetadata.CreateFromFile(string)"/> API when creating multiple references to the same assembly.
/// Reusing <see cref="AssemblyMetadata"/> object allows for sharing data across these references.
/// </para>
/// </remarks>
[Obsolete("Use CreateFromFile(assembly.Location) instead", error: true)]
[EditorBrowsable(EditorBrowsableState.Never)]
public static MetadataReference CreateFromAssembly(
Assembly assembly,
MetadataReferenceProperties properties,
DocumentationProvider? documentation = null)
{
return CreateFromAssemblyInternal(assembly, properties, documentation);
}
internal static string GetAssemblyFilePath(
Assembly assembly,
MetadataReferenceProperties properties)
{
if (assembly == null)
{
throw new ArgumentNullException(nameof(assembly));
}
if (assembly.IsDynamic)
{
throw new NotSupportedException(CodeAnalysisResources.CantCreateReferenceToDynamicAssembly);
}
if (properties.Kind != MetadataImageKind.Assembly)
{
throw new ArgumentException(CodeAnalysisResources.CantCreateModuleReferenceToAssembly, nameof(properties));
}
string location = assembly.Location;
if (string.IsNullOrEmpty(location))
{
throw new NotSupportedException(CodeAnalysisResources.CantCreateReferenceToAssemblyWithoutLocation);
}
return location;
}
internal static MetadataImageReference CreateFromAssemblyInternal(
Assembly assembly,
MetadataReferenceProperties properties,
DocumentationProvider? documentation = null)
{
var filePath = GetAssemblyFilePath(assembly, properties);
var peStream = StandardFileSystem.Instance.OpenFileWithNormalizedException(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
// The file is locked by the CLR assembly loader, so we can create a lazily read metadata,
// which might also lock the file until the reference is GC'd.
return CreateFromFile(peStream, filePath, PEStreamOptions.Default, properties, documentation);
}
internal static bool HasMetadata(Assembly assembly)
{
return !assembly.IsDynamic && !string.IsNullOrEmpty(assembly.Location);
}
}
}
|