File: Symbols\Metadata\PE\PEAssemblySymbol.cs
Web Access
Project: src\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// 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.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
 
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.Threading;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE
{
    /// <summary>
    /// Represents an assembly imported from a PE.
    /// </summary>
    internal sealed class PEAssemblySymbol : MetadataOrSourceAssemblySymbol
    {
        /// <summary>
        /// An Assembly object providing metadata for the assembly.
        /// </summary>
        private readonly PEAssembly _assembly;
 
        /// <summary>
        /// A DocumentationProvider that provides XML documentation comments for this assembly.
        /// </summary>
        private readonly DocumentationProvider _documentationProvider;
 
        /// <summary>
        /// The list of contained PEModuleSymbol objects.
        /// The list doesn't use type ReadOnlyCollection(Of PEModuleSymbol) so that we
        /// can return it from Modules property as is.
        /// </summary>
        private readonly ImmutableArray<ModuleSymbol> _modules;
 
        /// <summary>
        /// An array of assemblies involved in canonical type resolution of
        /// NoPia local types defined within this assembly. In other words, all 
        /// references used by a compilation referencing this assembly.
        /// The array and its content is provided by ReferenceManager and must not be modified.
        /// </summary>
        private ImmutableArray<AssemblySymbol> _noPiaResolutionAssemblies;
 
        /// <summary>
        /// An array of assemblies referenced by this assembly, which are linked (/l-ed) by 
        /// each compilation that is using this AssemblySymbol as a reference. 
        /// If this AssemblySymbol is linked too, it will be in this array too.
        /// The array and its content is provided by ReferenceManager and must not be modified.
        /// </summary>
        private ImmutableArray<AssemblySymbol> _linkedReferencedAssemblies;
 
        /// <summary>
        /// Assembly is /l-ed by compilation that is using it as a reference.
        /// </summary>
        private readonly bool _isLinked;
 
        /// <summary>
        /// Assembly's custom attributes
        /// </summary>
        private ImmutableArray<CSharpAttributeData> _lazyCustomAttributes;
 
#nullable enable
        private DiagnosticInfo? _lazyCachedCompilerFeatureRequiredDiagnosticInfo = CSDiagnosticInfo.EmptyErrorInfo;
 
        private ObsoleteAttributeData? _lazyObsoleteAttributeData = ObsoleteAttributeData.Uninitialized;
#nullable disable
 
        internal PEAssemblySymbol(PEAssembly assembly, DocumentationProvider documentationProvider, bool isLinked, MetadataImportOptions importOptions)
        {
            Debug.Assert(assembly != null);
            Debug.Assert(documentationProvider != null);
            _assembly = assembly;
            _documentationProvider = documentationProvider;
 
            var modules = new ModuleSymbol[assembly.Modules.Length];
 
            for (int i = 0; i < assembly.Modules.Length; i++)
            {
                modules[i] = new PEModuleSymbol(this, assembly.Modules[i], importOptions, i);
            }
 
            _modules = modules.AsImmutableOrNull();
            _isLinked = isLinked;
        }
 
        internal PEAssembly Assembly
        {
            get
            {
                return _assembly;
            }
        }
 
        public override AssemblyIdentity Identity
        {
            get
            {
                return _assembly.Identity;
            }
        }
 
        // TODO: https://github.com/dotnet/roslyn/issues/9000
        public override Version AssemblyVersionPattern => null;
 
        public override ImmutableArray<ModuleSymbol> Modules
        {
            get
            {
                return _modules;
            }
        }
 
        public override ImmutableArray<Location> Locations
        {
            get
            {
                return this.PrimaryModule.MetadataLocation.Cast<MetadataLocation, Location>();
            }
        }
 
        public override int MetadataToken
        {
            get
            {
                return MetadataTokens.GetToken(_assembly.Handle);
            }
        }
 
        internal override bool HasImportedFromTypeLibAttribute
            => PrimaryModule.Module.HasImportedFromTypeLibAttribute(Assembly.Handle, out _);
 
        internal override bool HasPrimaryInteropAssemblyAttribute
            => PrimaryModule.Module.HasPrimaryInteropAssemblyAttribute(Assembly.Handle, out _, out _);
 
        public override ImmutableArray<CSharpAttributeData> GetAttributes()
        {
            if (_lazyCustomAttributes.IsDefault)
            {
                if (this.MightContainExtensionMethods)
                {
                    this.PrimaryModule.LoadCustomAttributesFilterExtensions(_assembly.Handle,
                        ref _lazyCustomAttributes);
                }
                else
                {
                    this.PrimaryModule.LoadCustomAttributes(_assembly.Handle,
                        ref _lazyCustomAttributes);
                }
            }
            return _lazyCustomAttributes;
        }
 
        /// <summary>
        /// Look up the assemblies to which the given metadata type is forwarded.
        /// </summary>
        /// <param name="emittedName"></param>
        /// <returns>
        /// The assemblies to which the given type is forwarded.
        /// </returns>
        /// <remarks>
        /// The returned assemblies may also forward the type.
        /// </remarks>
        internal (AssemblySymbol FirstSymbol, AssemblySymbol SecondSymbol) LookupAssembliesForForwardedMetadataType(ref MetadataTypeName emittedName)
        {
            // Look in the type forwarders of the primary module of this assembly, clr does not honor type forwarder
            // in non-primary modules.
 
            // Examine the type forwarders, but only from the primary module.
            return this.PrimaryModule.GetAssembliesForForwardedType(ref emittedName);
        }
 
        internal override IEnumerable<NamedTypeSymbol> GetAllTopLevelForwardedTypes()
        {
            return this.PrimaryModule.GetForwardedTypes();
        }
 
#nullable enable
 
        internal override NamedTypeSymbol? TryLookupForwardedMetadataTypeWithCycleDetection(ref MetadataTypeName emittedName, ConsList<AssemblySymbol>? visitedAssemblies)
        {
            // Check if it is a forwarded type.
            (AssemblySymbol firstSymbol, AssemblySymbol secondSymbol) = LookupAssembliesForForwardedMetadataType(ref emittedName);
 
            if ((object)firstSymbol != null)
            {
                if ((object)secondSymbol != null)
                {
                    // Report the main module as that is the only one checked. clr does not honor type forwarders in non-primary modules.
                    return CreateMultipleForwardingErrorTypeSymbol(ref emittedName, this.PrimaryModule, firstSymbol, secondSymbol);
                }
 
                // Don't bother to check the forwarded-to assembly if we've already seen it.
                if (visitedAssemblies != null && visitedAssemblies.Contains(firstSymbol))
                {
                    return CreateCycleInTypeForwarderErrorTypeSymbol(ref emittedName);
                }
                else
                {
                    visitedAssemblies = new ConsList<AssemblySymbol>(this, visitedAssemblies ?? ConsList<AssemblySymbol>.Empty);
                    return firstSymbol.LookupDeclaredOrForwardedTopLevelMetadataType(ref emittedName, visitedAssemblies);
                }
            }
 
            return null;
        }
 
#nullable disable
 
        internal override ImmutableArray<AssemblySymbol> GetNoPiaResolutionAssemblies()
        {
            return _noPiaResolutionAssemblies;
        }
 
        internal override void SetNoPiaResolutionAssemblies(ImmutableArray<AssemblySymbol> assemblies)
        {
            _noPiaResolutionAssemblies = assemblies;
        }
 
        internal override void SetLinkedReferencedAssemblies(ImmutableArray<AssemblySymbol> assemblies)
        {
            _linkedReferencedAssemblies = assemblies;
        }
 
        internal override ImmutableArray<AssemblySymbol> GetLinkedReferencedAssemblies()
        {
            return _linkedReferencedAssemblies;
        }
 
        internal override ImmutableArray<byte> PublicKey
        {
            get
            {
                return Identity.PublicKey;
            }
        }
 
        internal override bool GetGuidString(out string guidString)
        {
            return Assembly.Modules[0].HasGuidAttribute(Assembly.Handle, out guidString);
        }
 
        internal override bool AreInternalsVisibleToThisAssembly(AssemblySymbol potentialGiverOfAccess)
        {
            IVTConclusion conclusion = MakeFinalIVTDetermination(potentialGiverOfAccess);
            return conclusion == IVTConclusion.Match || conclusion == IVTConclusion.OneSignedOneNot;
        }
 
        internal override IEnumerable<ImmutableArray<byte>> GetInternalsVisibleToPublicKeys(string simpleName)
        {
            return Assembly.GetInternalsVisibleToPublicKeys(simpleName);
        }
 
        internal override IEnumerable<string> GetInternalsVisibleToAssemblyNames()
        {
            return Assembly.GetInternalsVisibleToAssemblyNames();
        }
 
        internal DocumentationProvider DocumentationProvider
        {
            get
            {
                return _documentationProvider;
            }
        }
 
        internal override bool IsLinked
        {
            get
            {
                return _isLinked;
            }
        }
 
        public override bool MightContainExtensionMethods
        {
            get
            {
                // While the specification for ExtensionAttribute requires that the containing assembly
                // have the attribute if any type in the assembly has the attribute, some compilers do
                // not properly follow that spec. Therefore we pessimistically assume every assembly
                // may contain extension methods.
                return true;
            }
        }
 
        internal PEModuleSymbol PrimaryModule
        {
            get
            {
                return (PEModuleSymbol)_modules[0];
            }
        }
 
        internal sealed override CSharpCompilation DeclaringCompilation // perf, not correctness
        {
            get { return null; }
        }
 
        public override AssemblyMetadata GetMetadata() => _assembly.GetNonDisposableMetadata();
 
#nullable enable
        internal DiagnosticInfo? GetCompilerFeatureRequiredDiagnostic()
        {
            if (_lazyCachedCompilerFeatureRequiredDiagnosticInfo == CSDiagnosticInfo.EmptyErrorInfo)
            {
                Interlocked.CompareExchange(
                    ref _lazyCachedCompilerFeatureRequiredDiagnosticInfo,
                    PEUtilities.DeriveCompilerFeatureRequiredAttributeDiagnostic(this, PrimaryModule, this.Assembly.Handle, CompilerFeatureRequiredFeatures.None, new MetadataDecoder(PrimaryModule)),
                    CSDiagnosticInfo.EmptyErrorInfo);
            }
 
            return _lazyCachedCompilerFeatureRequiredDiagnosticInfo;
        }
 
        public override bool HasUnsupportedMetadata
            => GetCompilerFeatureRequiredDiagnostic()?.Code == (int)ErrorCode.ERR_UnsupportedCompilerFeature || base.HasUnsupportedMetadata;
 
        internal sealed override ObsoleteAttributeData? ObsoleteAttributeData
        {
            get
            {
                if (_lazyObsoleteAttributeData == ObsoleteAttributeData.Uninitialized)
                {
                    var experimentalData = PrimaryModule.Module.TryDecodeExperimentalAttributeData(Assembly.Handle, new MetadataDecoder(PrimaryModule));
                    Interlocked.CompareExchange(ref _lazyObsoleteAttributeData, experimentalData, ObsoleteAttributeData.Uninitialized);
                }
 
                return _lazyObsoleteAttributeData;
            }
        }
    }
}