File: Symbols\Source\SourceModuleSymbol.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.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    /// <summary>
    /// Represents the primary module of an assembly being built by compiler.
    /// </summary>
    internal sealed class SourceModuleSymbol : NonMissingModuleSymbol, IAttributeTargetSymbol
    {
        /// <summary>
        /// Owning assembly.
        /// </summary>
        private readonly SourceAssemblySymbol _assemblySymbol;
 
        private ImmutableArray<AssemblySymbol> _lazyAssembliesToEmbedTypesFrom;
 
        private ThreeState _lazyContainsExplicitDefinitionOfNoPiaLocalTypes = ThreeState.Unknown;
 
        /// <summary>
        /// The declarations corresponding to the source files of this module.
        /// </summary>
        private readonly DeclarationTable _sources;
 
        private SymbolCompletionState _state;
        private CustomAttributesBag<CSharpAttributeData> _lazyCustomAttributesBag;
        private ImmutableArray<Location> _locations;
        private NamespaceSymbol _globalNamespace;
 
        private bool _hasBadAttributes;
 
        private ThreeState _lazyUseUpdatedEscapeRules;
        private ThreeState _lazyRequiresRefSafetyRulesAttribute;
 
        internal SourceModuleSymbol(
            SourceAssemblySymbol assemblySymbol,
            DeclarationTable declarations,
            string moduleName)
        {
            Debug.Assert((object)assemblySymbol != null);
 
            _assemblySymbol = assemblySymbol;
            _sources = declarations;
            _name = moduleName;
        }
 
        internal void RecordPresenceOfBadAttributes()
        {
            _hasBadAttributes = true;
        }
 
        internal bool HasBadAttributes
        {
            get
            {
                return _hasBadAttributes;
            }
        }
 
        internal override int Ordinal
        {
            get
            {
                return 0;
            }
        }
 
        internal override Machine Machine
        {
            get
            {
                switch (DeclaringCompilation.Options.Platform)
                {
                    case Platform.Arm:
                        return Machine.ArmThumb2;
                    case Platform.X64:
                        return Machine.Amd64;
                    case Platform.Arm64:
                        return Machine.Arm64;
                    case Platform.Itanium:
                        return Machine.IA64;
                    default:
                        return Machine.I386;
                }
            }
        }
 
        internal override bool Bit32Required
        {
            get
            {
                return DeclaringCompilation.Options.Platform == Platform.X86;
            }
        }
 
        internal bool AnyReferencedAssembliesAreLinked
        {
            get
            {
                return GetAssembliesToEmbedTypesFrom().Length > 0;
            }
        }
 
        internal bool MightContainNoPiaLocalTypes()
        {
            return AnyReferencedAssembliesAreLinked ||
                ContainsExplicitDefinitionOfNoPiaLocalTypes;
        }
 
        internal ImmutableArray<AssemblySymbol> GetAssembliesToEmbedTypesFrom()
        {
            if (_lazyAssembliesToEmbedTypesFrom.IsDefault)
            {
                AssertReferencesInitialized();
                var buffer = ArrayBuilder<AssemblySymbol>.GetInstance();
 
                foreach (AssemblySymbol asm in this.GetReferencedAssemblySymbols())
                {
                    if (asm.IsLinked)
                    {
                        buffer.Add(asm);
                    }
                }
 
                ImmutableInterlocked.InterlockedCompareExchange(ref _lazyAssembliesToEmbedTypesFrom,
                                                    buffer.ToImmutableAndFree(),
                                                    default(ImmutableArray<AssemblySymbol>));
            }
 
            Debug.Assert(!_lazyAssembliesToEmbedTypesFrom.IsDefault);
            return _lazyAssembliesToEmbedTypesFrom;
        }
 
        internal bool ContainsExplicitDefinitionOfNoPiaLocalTypes
        {
            get
            {
                if (_lazyContainsExplicitDefinitionOfNoPiaLocalTypes == ThreeState.Unknown)
                {
                    _lazyContainsExplicitDefinitionOfNoPiaLocalTypes = NamespaceContainsExplicitDefinitionOfNoPiaLocalTypes(GlobalNamespace).ToThreeState();
                }
 
                Debug.Assert(_lazyContainsExplicitDefinitionOfNoPiaLocalTypes != ThreeState.Unknown);
                return _lazyContainsExplicitDefinitionOfNoPiaLocalTypes == ThreeState.True;
            }
        }
 
        private static bool NamespaceContainsExplicitDefinitionOfNoPiaLocalTypes(NamespaceSymbol ns)
        {
            foreach (Symbol s in ns.GetMembersUnordered())
            {
                switch (s.Kind)
                {
                    case SymbolKind.Namespace:
                        if (NamespaceContainsExplicitDefinitionOfNoPiaLocalTypes((NamespaceSymbol)s))
                        {
                            return true;
                        }
 
                        break;
 
                    case SymbolKind.NamedType:
                        if (((NamedTypeSymbol)s).IsExplicitDefinitionOfNoPiaLocalType)
                        {
                            return true;
                        }
 
                        break;
                }
            }
 
            return false;
        }
 
        public override NamespaceSymbol GlobalNamespace
        {
            get
            {
                if ((object)_globalNamespace == null)
                {
                    var diagnostics = BindingDiagnosticBag.GetInstance();
                    var globalNS = new SourceNamespaceSymbol(
                        this, this, DeclaringCompilation.MergedRootDeclaration, diagnostics);
 
                    if (Interlocked.CompareExchange(ref _globalNamespace, globalNS, null) == null)
                    {
                        this.AddDeclarationDiagnostics(diagnostics);
                    }
 
                    diagnostics.Free();
                }
 
                return _globalNamespace;
            }
        }
 
        internal sealed override bool RequiresCompletion
        {
            get { return true; }
        }
 
        internal sealed override bool HasComplete(CompletionPart part)
        {
            return _state.HasComplete(part);
        }
 
#nullable enable
        internal override void ForceComplete(SourceLocation? locationOpt, Predicate<Symbol>? filter, CancellationToken cancellationToken)
        {
            while (true)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var incompletePart = _state.NextIncompletePart;
                switch (incompletePart)
                {
                    case CompletionPart.Attributes:
                        GetAttributes();
                        break;
 
                    case CompletionPart.StartValidatingReferencedAssemblies:
                        {
                            BindingDiagnosticBag? diagnostics = null;
 
                            if (AnyReferencedAssembliesAreLinked)
                            {
                                diagnostics = BindingDiagnosticBag.GetInstance();
                                ValidateLinkedAssemblies(diagnostics, cancellationToken);
                            }
 
                            if (_state.NotePartComplete(CompletionPart.StartValidatingReferencedAssemblies))
                            {
                                if (diagnostics != null)
                                {
                                    _assemblySymbol.AddDeclarationDiagnostics(diagnostics);
                                }
 
                                _state.NotePartComplete(CompletionPart.FinishValidatingReferencedAssemblies);
                            }
 
                            if (diagnostics != null)
                            {
                                diagnostics.Free();
                            }
                        }
                        break;
 
                    case CompletionPart.FinishValidatingReferencedAssemblies:
                        // some other thread has started validating references (otherwise we would be in the case above) so
                        // we just wait for it to both finish and report the diagnostics.
                        Debug.Assert(_state.HasComplete(CompletionPart.StartValidatingReferencedAssemblies));
                        _state.SpinWaitComplete(CompletionPart.FinishValidatingReferencedAssemblies, cancellationToken);
                        break;
 
                    case CompletionPart.MembersCompleted:
                        this.GlobalNamespace.ForceComplete(locationOpt, filter, cancellationToken);
 
                        if (this.GlobalNamespace.HasComplete(CompletionPart.MembersCompleted))
                        {
                            // Completing the global namespace members means all InterceptsLocationAttributes have been bound.
                            Volatile.Write(ref DeclaringCompilation.InterceptorsDiscoveryComplete, true);
 
                            _state.NotePartComplete(CompletionPart.MembersCompleted);
                        }
                        else
                        {
                            Debug.Assert(locationOpt != null || filter != null, "If no location or filter was specified, then the namespace members should be completed");
                            return;
                        }
 
                        break;
 
                    case CompletionPart.None:
                        return;
 
                    default:
                        // any other values are completion parts intended for other kinds of symbols
                        _state.NotePartComplete(incompletePart);
                        break;
                }
 
                _state.SpinWaitComplete(incompletePart, cancellationToken);
            }
        }
 
        private void ValidateLinkedAssemblies(BindingDiagnosticBag diagnostics, CancellationToken cancellationToken)
        {
            foreach (AssemblySymbol a in GetReferencedAssemblySymbols())
            {
                cancellationToken.ThrowIfCancellationRequested();
 
                if (!a.IsMissing && a.IsLinked)
                {
                    if (!a.GetGuidString(out _))
                    {
                        // ERRID_PIAHasNoAssemblyGuid1/ERR_NoPIAAssemblyMissingAttribute
                        diagnostics.Add(ErrorCode.ERR_NoPIAAssemblyMissingAttribute, NoLocation.Singleton, a, AttributeDescription.GuidAttribute.FullName);
                    }
 
                    if (!a.HasImportedFromTypeLibAttribute && !a.HasPrimaryInteropAssemblyAttribute)
                    {
                        // ERRID_PIAHasNoTypeLibAttribute1/ERR_NoPIAAssemblyMissingAttributes
                        diagnostics.Add(ErrorCode.ERR_NoPIAAssemblyMissingAttributes, NoLocation.Singleton, a,
                                                   AttributeDescription.ImportedFromTypeLibAttribute.FullName,
                                                   AttributeDescription.PrimaryInteropAssemblyAttribute.FullName);
                    }
                }
            }
        }
 
        internal void DiscoverInterceptorsIfNeeded()
        {
            if (!Volatile.Read(ref DeclaringCompilation.InterceptorsDiscoveryComplete))
            {
                discoverInterceptors();
                Volatile.Write(ref DeclaringCompilation.InterceptorsDiscoveryComplete, true);
            }
 
            void discoverInterceptors()
            {
                var location = this.GlobalNamespace.GetFirstLocationOrNone();
                if (!location.IsInSource)
                {
                    return;
                }
 
                var toVisit = ArrayBuilder<NamespaceOrTypeSymbol>.GetInstance();
 
                // Search the namespaces which were indicated to contain interceptors.
                ImmutableArray<ImmutableArray<string>> interceptorsNamespaces = ((CSharpParseOptions)location.SourceTree.Options).InterceptorsNamespaces;
                foreach (ImmutableArray<string> namespaceParts in interceptorsNamespaces)
                {
                    if (namespaceParts is ["global"])
                    {
                        toVisit.Clear();
                        toVisit.Add(GlobalNamespace);
                        // No point in continuing, we already are going to search the entire module in this case.
                        break;
                    }
 
                    var cursor = GlobalNamespace;
                    foreach (string namespacePart in namespaceParts)
                    {
                        cursor = (NamespaceSymbol?)cursor.GetNestedNamespace(namespacePart);
                        if (cursor is null)
                        {
                            break;
                        }
                    }
 
                    if (cursor is not null)
                    {
                        toVisit.Add(cursor);
                    }
                }
 
                while (toVisit.Count > 0)
                {
                    var item = toVisit.Pop();
                    if (item is SourceMemberContainerTypeSymbol type)
                    {
                        type.DiscoverInterceptors(toVisit);
                    }
                    else if (item is SourceNamespaceSymbol @namespace)
                    {
                        foreach (var member in @namespace.GetMembers())
                        {
                            if (member is not NamespaceOrTypeSymbol namespaceOrType)
                            {
                                throw ExceptionUtilities.UnexpectedValue(member);
                            }
 
                            toVisit.Add(namespaceOrType);
                        }
                    }
                    else
                    {
                        throw ExceptionUtilities.UnexpectedValue(item);
                    }
                }
 
                toVisit.Free();
            }
        }
#nullable disable
 
        public override ImmutableArray<Location> Locations
        {
            get
            {
                if (_locations.IsDefault)
                {
                    ImmutableInterlocked.InterlockedInitialize(
                        ref _locations,
                        DeclaringCompilation.MergedRootDeclaration.Declarations.SelectAsArray(d => (Location)d.Location));
                }
 
                return _locations;
            }
        }
 
        /// <summary>
        /// The name (contains extension)
        /// </summary>
        private readonly string _name;
 
        public override string Name
        {
            get
            {
                return _name;
            }
        }
 
        public override Symbol ContainingSymbol
        {
            get
            {
                return _assemblySymbol;
            }
        }
 
        public override AssemblySymbol ContainingAssembly
        {
            get
            {
                return _assemblySymbol;
            }
        }
 
        internal SourceAssemblySymbol ContainingSourceAssembly
        {
            get
            {
                return _assemblySymbol;
            }
        }
 
        /// <remarks>
        /// This override is essential - it's a base case of the recursive definition.
        /// </remarks>
        internal override CSharpCompilation DeclaringCompilation
        {
            get
            {
                return _assemblySymbol.DeclaringCompilation;
            }
        }
 
        internal override ICollection<string> TypeNames
        {
            get
            {
                return _sources.TypeNames;
            }
        }
 
        internal override ICollection<string> NamespaceNames
        {
            get
            {
                return _sources.NamespaceNames;
            }
        }
 
        IAttributeTargetSymbol IAttributeTargetSymbol.AttributesOwner
        {
            get { return _assemblySymbol; }
        }
 
        AttributeLocation IAttributeTargetSymbol.DefaultAttributeLocation
        {
            get { return AttributeLocation.Module; }
        }
 
        AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations
        {
            get
            {
                return ContainingAssembly.IsInteractive ? AttributeLocation.None : AttributeLocation.Assembly | AttributeLocation.Module;
            }
        }
 
        /// <summary>
        /// Returns a bag of applied custom attributes and data decoded from well-known attributes. Returns null if there are no attributes applied on the symbol.
        /// </summary>
        /// <remarks>
        /// Forces binding and decoding of attributes.
        /// </remarks>
        private CustomAttributesBag<CSharpAttributeData> GetAttributesBag()
        {
            if (_lazyCustomAttributesBag == null || !_lazyCustomAttributesBag.IsSealed)
            {
                var mergedAttributes = ((SourceAssemblySymbol)this.ContainingAssembly).GetAttributeDeclarations();
                if (LoadAndValidateAttributes(OneOrMany.Create(mergedAttributes), ref _lazyCustomAttributesBag))
                {
                    var completed = _state.NotePartComplete(CompletionPart.Attributes);
                    Debug.Assert(completed);
                }
            }
 
            return _lazyCustomAttributesBag;
        }
 
        /// <summary>
        /// Gets the attributes applied on this symbol.
        /// Returns an empty array if there are no attributes.
        /// </summary>
        /// <remarks>
        /// NOTE: This method should always be kept as a sealed override.
        /// If you want to override attribute binding logic for a sub-class, then override <see cref="GetAttributesBag"/> method.
        /// </remarks>
        public sealed override ImmutableArray<CSharpAttributeData> GetAttributes()
        {
            return this.GetAttributesBag().Attributes;
        }
 
        /// <summary>
        /// Returns data decoded from well-known attributes applied to the symbol or null if there are no applied attributes.
        /// </summary>
        /// <remarks>
        /// Forces binding and decoding of attributes.
        /// </remarks>
        private ModuleWellKnownAttributeData GetDecodedWellKnownAttributeData()
        {
            var attributesBag = _lazyCustomAttributesBag;
            if (attributesBag == null || !attributesBag.IsDecodedWellKnownAttributeDataComputed)
            {
                attributesBag = this.GetAttributesBag();
            }
 
            return (ModuleWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData;
        }
 
        protected override void DecodeWellKnownAttributeImpl(ref DecodeWellKnownAttributeArguments<AttributeSyntax, CSharpAttributeData, AttributeLocation> arguments)
        {
            Debug.Assert((object)arguments.AttributeSyntaxOpt != null);
 
            var attribute = arguments.Attribute;
            Debug.Assert(!attribute.HasErrors);
            Debug.Assert(arguments.SymbolPart == AttributeLocation.None);
 
            if (attribute.IsTargetAttribute(AttributeDescription.DefaultCharSetAttribute))
            {
                CharSet charSet = attribute.GetConstructorArgument<CharSet>(0, SpecialType.System_Enum);
                if (!ModuleWellKnownAttributeData.IsValidCharSet(charSet))
                {
                    ((BindingDiagnosticBag)arguments.Diagnostics).Add(ErrorCode.ERR_InvalidAttributeArgument, attribute.GetAttributeArgumentLocation(0), arguments.AttributeSyntaxOpt.GetErrorDisplayName());
                }
                else
                {
                    arguments.GetOrCreateData<ModuleWellKnownAttributeData>().DefaultCharacterSet = charSet;
                }
            }
            else if (ReportExplicitUseOfReservedAttributes(in arguments,
                ReservedAttributes.NullableContextAttribute | ReservedAttributes.NullablePublicOnlyAttribute | ReservedAttributes.RefSafetyRulesAttribute))
            {
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.SkipLocalsInitAttribute))
            {
                CSharpAttributeData.DecodeSkipLocalsInitAttribute<ModuleWellKnownAttributeData>(DeclaringCompilation, ref arguments);
            }
            else if (attribute.IsTargetAttribute(AttributeDescription.ExperimentalAttribute))
            {
                arguments.GetOrCreateData<ModuleWellKnownAttributeData>().ExperimentalAttributeData = attribute.DecodeExperimentalAttribute();
            }
        }
 
#nullable enable
 
        internal bool RequiresRefSafetyRulesAttribute()
        {
            if (_lazyRequiresRefSafetyRulesAttribute == ThreeState.Unknown)
            {
                bool value = UseUpdatedEscapeRules &&
                    !isFeatureDisabled(_assemblySymbol.DeclaringCompilation) &&
                    namespaceIncludesTypeDeclarations(GlobalNamespace);
                _lazyRequiresRefSafetyRulesAttribute = value.ToThreeState();
            }
            return _lazyRequiresRefSafetyRulesAttribute.Value();
 
            static bool isFeatureDisabled(CSharpCompilation compilation)
            {
                var options = (CSharpParseOptions?)compilation.SyntaxTrees.FirstOrDefault()?.Options;
                return options?.Features?.ContainsKey("noRefSafetyRulesAttribute") == true;
            }
 
            static bool namespaceIncludesTypeDeclarations(NamespaceSymbol ns)
            {
                foreach (var member in ns.GetMembersUnordered())
                {
                    switch (member.Kind)
                    {
                        case SymbolKind.Namespace:
                            if (namespaceIncludesTypeDeclarations((NamespaceSymbol)member))
                            {
                                return true;
                            }
                            break;
                        case SymbolKind.NamedType:
                            return true;
                    }
                }
                return false;
            }
        }
 
        internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder<SynthesizedAttributeData> attributes)
        {
            base.AddSynthesizedAttributes(moduleBuilder, ref attributes);
 
            var compilation = _assemblySymbol.DeclaringCompilation;
            if (compilation.Options.AllowUnsafe)
            {
                // NOTE: GlobalAttrBind::EmitCompilerGeneratedAttrs skips attribute if the well-known type isn't available.
                if (!(compilation.GetWellKnownType(WellKnownType.System_Security_UnverifiableCodeAttribute) is MissingMetadataTypeSymbol))
                {
                    AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(
                        WellKnownMember.System_Security_UnverifiableCodeAttribute__ctor));
                }
            }
 
            if (RequiresRefSafetyRulesAttribute())
            {
                var version = ImmutableArray.Create(new TypedConstant(compilation.GetSpecialType(SpecialType.System_Int32), TypedConstantKind.Primitive, 11));
                AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeRefSafetyRulesAttribute(version));
            }
 
            if (moduleBuilder.ShouldEmitNullablePublicOnlyAttribute())
            {
                var includesInternals = ImmutableArray.Create(
                    new TypedConstant(compilation.GetSpecialType(SpecialType.System_Boolean), TypedConstantKind.Primitive, _assemblySymbol.InternalsAreVisible));
                AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullablePublicOnlyAttribute(includesInternals));
            }
        }
 
        internal override bool HasAssemblyCompilationRelaxationsAttribute
        {
            get
            {
                CommonAssemblyWellKnownAttributeData<NamedTypeSymbol> decodedData = ((SourceAssemblySymbol)this.ContainingAssembly).GetSourceDecodedWellKnownAttributeData();
                return decodedData != null && decodedData.HasCompilationRelaxationsAttribute;
            }
        }
 
        internal override bool HasAssemblyRuntimeCompatibilityAttribute
        {
            get
            {
                CommonAssemblyWellKnownAttributeData<NamedTypeSymbol> decodedData = ((SourceAssemblySymbol)this.ContainingAssembly).GetSourceDecodedWellKnownAttributeData();
                return decodedData != null && decodedData.HasRuntimeCompatibilityAttribute;
            }
        }
 
        internal override CharSet? DefaultMarshallingCharSet
        {
            get
            {
                var data = GetDecodedWellKnownAttributeData();
                return data != null && data.HasDefaultCharSetAttribute ? data.DefaultCharacterSet : (CharSet?)null;
            }
        }
 
        public sealed override bool AreLocalsZeroed
        {
            get
            {
                var data = GetDecodedWellKnownAttributeData();
                return data?.HasSkipLocalsInitAttribute != true;
            }
        }
 
        public override ModuleMetadata? GetMetadata() => null;
 
        internal override bool UseUpdatedEscapeRules
        {
            get
            {
                if (_lazyUseUpdatedEscapeRules == ThreeState.Unknown)
                {
                    var compilation = _assemblySymbol.DeclaringCompilation;
                    bool value = compilation.IsFeatureEnabled(MessageID.IDS_FeatureRefFields) || _assemblySymbol.RuntimeSupportsByRefFields;
                    _lazyUseUpdatedEscapeRules = value.ToThreeState();
                }
                return _lazyUseUpdatedEscapeRules == ThreeState.True;
            }
        }
 
        /// <summary>
        /// Returns data decoded from <see cref="ObsoleteAttribute"/> attribute or null if there is no <see cref="ObsoleteAttribute"/> attribute.
        /// This property returns <see cref="Microsoft.CodeAnalysis.ObsoleteAttributeData.Uninitialized"/> if attribute arguments haven't been decoded yet.
        /// </summary>
        internal sealed override ObsoleteAttributeData? ObsoleteAttributeData
        {
            get
            {
                var attributesBag = _lazyCustomAttributesBag;
                if (attributesBag != null && attributesBag.IsDecodedWellKnownAttributeDataComputed)
                {
                    var decodedData = (ModuleWellKnownAttributeData)attributesBag.DecodedWellKnownAttributeData;
                    return decodedData?.ExperimentalAttributeData;
                }
 
                var attributesDeclarations = ((SourceAssemblySymbol)ContainingAssembly).GetAttributeDeclarations();
                if (attributesDeclarations.IsEmpty)
                {
                    return null;
                }
 
                return ObsoleteAttributeData.Uninitialized;
            }
        }
    }
}