|
// 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.IO;
using System.Threading;
using System.Collections.Immutable;
using System.Collections.Generic;
namespace Microsoft.CodeAnalysis
{
/// <summary>
/// Reference to metadata stored in the standard ECMA-335 metadata format.
/// </summary>
public abstract class PortableExecutableReference : MetadataReference
{
private readonly string? _filePath;
private DocumentationProvider? _lazyDocumentation;
protected PortableExecutableReference(
MetadataReferenceProperties properties,
string? fullPath = null,
DocumentationProvider? initialDocumentation = null)
: base(properties)
{
_filePath = fullPath;
_lazyDocumentation = initialDocumentation;
}
/// <summary>
/// Display string used in error messages to identity the reference.
/// </summary>
public override string? Display
{
get { return FilePath; }
}
/// <summary>
/// Path describing the location of the metadata, or null if the metadata have no location.
/// </summary>
public string? FilePath
{
get { return _filePath; }
}
/// <summary>
/// XML documentation comments provider for the reference.
/// </summary>
internal DocumentationProvider DocumentationProvider
{
get
{
if (_lazyDocumentation == null)
{
Interlocked.CompareExchange(ref _lazyDocumentation, CreateDocumentationProvider(), null);
}
return _lazyDocumentation;
}
}
/// <summary>
/// Create documentation provider for the reference.
/// </summary>
/// <remarks>
/// Called when the compiler needs to read the documentation for the reference.
/// This method can be called multiple times from different threads. The result of one of the calls
/// is cached on the reference object.
/// </remarks>
protected abstract DocumentationProvider CreateDocumentationProvider();
/// <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 new PortableExecutableReference WithAliases(IEnumerable<string> aliases)
{
return this.WithAliases(ImmutableArray.CreateRange(aliases));
}
/// <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 new PortableExecutableReference WithAliases(ImmutableArray<string> aliases)
{
return WithProperties(Properties.WithAliases(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 new PortableExecutableReference WithEmbedInteropTypes(bool value)
{
return WithProperties(Properties.WithEmbedInteropTypes(value));
}
/// <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 new PortableExecutableReference WithProperties(MetadataReferenceProperties properties)
{
if (properties == this.Properties)
{
return this;
}
return WithPropertiesImpl(properties);
}
internal sealed override MetadataReference WithPropertiesImplReturningMetadataReference(MetadataReferenceProperties properties)
{
return WithPropertiesImpl(properties);
}
/// <summary>
/// Returns an instance of the reference with specified properties.
/// </summary>
/// <param name="properties">The new properties for the reference.</param>
/// <exception cref="NotSupportedException">Specified values not supported.</exception>
/// <remarks>Only invoked if the properties changed.</remarks>
protected abstract PortableExecutableReference WithPropertiesImpl(MetadataReferenceProperties properties);
/// <summary>
/// Get metadata representation for the PE file.
/// </summary>
/// <exception cref="BadImageFormatException">If the PE image format is invalid.</exception>
/// <exception cref="IOException">The metadata image content can't be read.</exception>
/// <exception cref="FileNotFoundException">The metadata image is stored in a file that can't be found.</exception>
/// <remarks>
/// Called when the <see cref="Compilation"/> needs to read the reference metadata.
///
/// The listed exceptions are caught and converted to compilation diagnostics.
/// Any other exception is considered an unexpected error in the implementation and is not caught.
///
/// <see cref="Metadata"/> objects may cache information decoded from the PE image.
/// Reusing <see cref="Metadata"/> instances across metadata references will result in better performance.
///
/// The calling <see cref="Compilation"/> doesn't take ownership of the <see cref="Metadata"/> objects returned by this method.
/// The implementation needs to retrieve the object from a provider that manages their lifetime (such as metadata cache).
/// The <see cref="Metadata"/> object is kept alive by the <see cref="Compilation"/> that called <see cref="GetMetadataNoCopy"/>
/// and by all compilations created from it via calls to With- factory methods on <see cref="Compilation"/>,
/// other than <see cref="Compilation.WithReferences(MetadataReference[])"/> overloads. A compilation created using
/// <see cref="Compilation.WithReferences(MetadataReference[])"/> will call to <see cref="GetMetadataNoCopy"/> again.
/// </remarks>
protected abstract Metadata GetMetadataImpl();
internal Metadata GetMetadataNoCopy()
{
return GetMetadataImpl();
}
/// <summary>
/// Returns a copy of the <see cref="Metadata"/> object this <see cref="PortableExecutableReference"/>
/// contains. This copy does not need to be <see cref="IDisposable.Dispose"/>d.
/// </summary>
/// <exception cref="BadImageFormatException">If the PE image format is invalid.</exception>
/// <exception cref="IOException">The metadata image content can't be read.</exception>
/// <exception cref="FileNotFoundException">The metadata image is stored in a file that can't be found.</exception>
public Metadata GetMetadata()
{
return GetMetadataNoCopy().Copy();
}
/// <summary>
/// Returns the <see cref="MetadataId"/> for this reference's <see cref="Metadata"/>.
/// This will be equivalent to calling <see cref="GetMetadata()"/>.<see cref="Metadata.Id"/>,
/// but can be done more efficiently.
/// </summary>
/// <exception cref="BadImageFormatException">If the PE image format is invalid.</exception>
/// <exception cref="IOException">The metadata image content can't be read.</exception>
/// <exception cref="FileNotFoundException">The metadata image is stored in a file that can't be found.</exception>
public MetadataId GetMetadataId()
{
return GetMetadataNoCopy().Id;
}
internal static Diagnostic ExceptionToDiagnostic(Exception e, CommonMessageProvider messageProvider, Location location, string display, MetadataImageKind kind)
{
if (e is BadImageFormatException)
{
int errorCode = (kind == MetadataImageKind.Assembly) ? messageProvider.ERR_InvalidAssemblyMetadata : messageProvider.ERR_InvalidModuleMetadata;
return messageProvider.CreateDiagnostic(errorCode, location, display, e.Message);
}
var fileNotFound = e as FileNotFoundException;
if (fileNotFound != null)
{
return messageProvider.CreateDiagnostic(messageProvider.ERR_MetadataFileNotFound, location, fileNotFound.FileName ?? string.Empty);
}
else
{
int errorCode = (kind == MetadataImageKind.Assembly) ? messageProvider.ERR_ErrorOpeningAssemblyFile : messageProvider.ERR_ErrorOpeningModuleFile;
return messageProvider.CreateDiagnostic(errorCode, location, display, e.Message);
}
}
}
}
|