File: Symbols\Retargeting\RetargetingModuleSymbol.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.Globalization;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using System.Threading;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting
{
    /// <summary>
    /// Represents a primary module of a <see cref="RetargetingAssemblySymbol"/>. Essentially this is a wrapper around 
    /// another <see cref="SourceModuleSymbol"/> that is responsible for retargeting symbols from one assembly to another. 
    /// It can retarget symbols for multiple assemblies at the same time.
    /// 
    /// Here is how retargeting is implemented in general:
    /// - Symbols from underlying module are substituted with retargeting symbols.
    /// - Symbols from referenced assemblies that can be reused as is (i.e. don't have to be retargeted) are
    ///   used as is.
    /// - Symbols from referenced assemblies that must be retargeted are substituted with result of retargeting.
    /// </summary>
    internal sealed partial class RetargetingModuleSymbol : NonMissingModuleSymbol
    {
        /// <summary>
        /// Owning <see cref="RetargetingAssemblySymbol"/>.
        /// </summary>
        private readonly RetargetingAssemblySymbol _retargetingAssembly;
 
        /// <summary>
        /// The underlying <see cref="ModuleSymbol"/>, cannot be another <see cref="RetargetingModuleSymbol"/>.
        /// </summary>
        private readonly SourceModuleSymbol _underlyingModule;
 
        /// <summary>
        /// The map that captures information about what assembly should be retargeted 
        /// to what assembly. Key is the <see cref="AssemblySymbol"/> referenced by the underlying module,
        /// value is the corresponding <see cref="AssemblySymbol"/> referenced by this module, and corresponding
        /// retargeting map for symbols.
        /// </summary>
        private readonly Dictionary<AssemblySymbol, DestinationData> _retargetingAssemblyMap =
            new Dictionary<AssemblySymbol, DestinationData>();
 
#nullable enable
 
        private struct DestinationData
        {
            public AssemblySymbol To;
            private ConcurrentDictionary<NamedTypeSymbol, NamedTypeSymbol>? _symbolMap;
 
            public ConcurrentDictionary<NamedTypeSymbol, NamedTypeSymbol> SymbolMap => LazyInitializer.EnsureInitialized(ref _symbolMap);
        }
 
#nullable disable
 
        internal readonly RetargetingSymbolTranslator RetargetingTranslator;
 
        /// <summary>
        /// Retargeted custom attributes
        /// </summary>
        private ImmutableArray<CSharpAttributeData> _lazyCustomAttributes;
 
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="retargetingAssembly">
        /// Owning assembly.
        /// </param>
        /// <param name="underlyingModule">
        /// The underlying ModuleSymbol, cannot be another RetargetingModuleSymbol.
        /// </param>
        public RetargetingModuleSymbol(RetargetingAssemblySymbol retargetingAssembly, SourceModuleSymbol underlyingModule)
        {
            Debug.Assert((object)retargetingAssembly != null);
            Debug.Assert((object)underlyingModule != null);
 
            _retargetingAssembly = retargetingAssembly;
            _underlyingModule = underlyingModule;
            this.RetargetingTranslator = new RetargetingSymbolTranslator(this);
 
            _createRetargetingMethod = CreateRetargetingMethod;
            _createRetargetingNamespace = CreateRetargetingNamespace;
            _createRetargetingNamedType = CreateRetargetingNamedType;
            _createRetargetingField = CreateRetargetingField;
            _createRetargetingProperty = CreateRetargetingProperty;
            _createRetargetingEvent = CreateRetargetingEvent;
            _createRetargetingTypeParameter = CreateRetargetingTypeParameter;
        }
 
        internal override int Ordinal
        {
            get
            {
                Debug.Assert(_underlyingModule.Ordinal == 0); // Always a source module
                return 0;
            }
        }
 
        internal override Machine Machine
        {
            get
            {
                return _underlyingModule.Machine;
            }
        }
 
        internal override bool Bit32Required
        {
            get
            {
                return _underlyingModule.Bit32Required;
            }
        }
 
        /// <summary>
        /// The underlying ModuleSymbol, cannot be another RetargetingModuleSymbol.
        /// </summary>
        public SourceModuleSymbol UnderlyingModule
        {
            get
            {
                return _underlyingModule;
            }
        }
 
        public override NamespaceSymbol GlobalNamespace
        {
            get
            {
                return RetargetingTranslator.Retarget(_underlyingModule.GlobalNamespace);
            }
        }
 
        public override bool IsImplicitlyDeclared
        {
            get { return _underlyingModule.IsImplicitlyDeclared; }
        }
 
        public override string Name
        {
            get
            {
                return _underlyingModule.Name;
            }
        }
 
        public override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken))
        {
            return _underlyingModule.GetDocumentationCommentXml(preferredCulture, expandIncludes, cancellationToken);
        }
 
        public override Symbol ContainingSymbol
        {
            get
            {
                return _retargetingAssembly;
            }
        }
 
        public override AssemblySymbol ContainingAssembly
        {
            get
            {
                return _retargetingAssembly;
            }
        }
 
        public override ImmutableArray<Location> Locations
        {
            get
            {
                return _underlyingModule.Locations;
            }
        }
 
        /// <summary>
        /// A helper method for ReferenceManager to set AssemblySymbols for assemblies 
        /// referenced by this module.
        /// </summary>
        internal override void SetReferences(ModuleReferences<AssemblySymbol> moduleReferences, SourceAssemblySymbol originatingSourceAssemblyDebugOnly)
        {
            base.SetReferences(moduleReferences, originatingSourceAssemblyDebugOnly);
 
            // Build the retargeting map
            _retargetingAssemblyMap.Clear();
 
            ImmutableArray<AssemblySymbol> underlyingBoundReferences = _underlyingModule.GetReferencedAssemblySymbols();
            ImmutableArray<AssemblySymbol> referencedAssemblySymbols = moduleReferences.Symbols;
 
            Debug.Assert(referencedAssemblySymbols.Length == moduleReferences.Identities.Length);
            Debug.Assert(referencedAssemblySymbols.Length <= underlyingBoundReferences.Length); // Linked references are filtered out.
 
            int i, j;
            for (i = 0, j = 0; i < referencedAssemblySymbols.Length; i++, j++)
            {
                // Skip linked assemblies for source module
                while (underlyingBoundReferences[j].IsLinked)
                {
                    j++;
                }
 
#if DEBUG
                var identityComparer = _underlyingModule.DeclaringCompilation.Options.AssemblyIdentityComparer;
                var definitionIdentity = ReferenceEquals(referencedAssemblySymbols[i], originatingSourceAssemblyDebugOnly) ?
                        new AssemblyIdentity(name: originatingSourceAssemblyDebugOnly.Name) :
                        referencedAssemblySymbols[i].Identity;
 
                Debug.Assert(identityComparer.Compare(moduleReferences.Identities[i], definitionIdentity) != AssemblyIdentityComparer.ComparisonResult.NotEquivalent);
                Debug.Assert(identityComparer.Compare(moduleReferences.Identities[i], underlyingBoundReferences[j].Identity) != AssemblyIdentityComparer.ComparisonResult.NotEquivalent);
#endif
 
                if (!ReferenceEquals(referencedAssemblySymbols[i], underlyingBoundReferences[j]))
                {
                    DestinationData destinationData;
 
                    if (!_retargetingAssemblyMap.TryGetValue(underlyingBoundReferences[j], out destinationData))
                    {
                        _retargetingAssemblyMap.Add(underlyingBoundReferences[j],
                            new DestinationData { To = referencedAssemblySymbols[i] });
                    }
                    else
                    {
                        Debug.Assert(ReferenceEquals(destinationData.To, referencedAssemblySymbols[i]));
                    }
                }
            }
 
#if DEBUG
            while (j < underlyingBoundReferences.Length && underlyingBoundReferences[j].IsLinked)
            {
                j++;
            }
 
            Debug.Assert(j == underlyingBoundReferences.Length);
#endif
        }
 
        internal bool RetargetingDefinitions(AssemblySymbol from, out AssemblySymbol to)
        {
            DestinationData destination;
 
            if (!_retargetingAssemblyMap.TryGetValue(from, out destination))
            {
                to = null;
                return false;
            }
 
            to = destination.To;
            return true;
        }
 
        internal override ICollection<string> TypeNames
        {
            get
            {
                return _underlyingModule.TypeNames;
            }
        }
 
        internal override ICollection<string> NamespaceNames
        {
            get
            {
                return _underlyingModule.NamespaceNames;
            }
        }
 
        public override ImmutableArray<CSharpAttributeData> GetAttributes()
        {
            return RetargetingTranslator.GetRetargetedAttributes(_underlyingModule.GetAttributes(), ref _lazyCustomAttributes);
        }
 
        internal override bool HasAssemblyCompilationRelaxationsAttribute
        {
            get
            {
                return _underlyingModule.HasAssemblyCompilationRelaxationsAttribute;
            }
        }
 
        internal override bool HasAssemblyRuntimeCompatibilityAttribute
        {
            get
            {
                return _underlyingModule.HasAssemblyRuntimeCompatibilityAttribute;
            }
        }
 
        internal override CharSet? DefaultMarshallingCharSet
        {
            get
            {
                return _underlyingModule.DefaultMarshallingCharSet;
            }
        }
 
        internal sealed override CSharpCompilation DeclaringCompilation // perf, not correctness
        {
            get { return null; }
        }
 
        public override ModuleMetadata GetMetadata() => _underlyingModule.GetMetadata();
 
        public sealed override bool AreLocalsZeroed
        {
            get
            {
                throw ExceptionUtilities.Unreachable();
            }
        }
 
        internal override bool UseUpdatedEscapeRules => _underlyingModule.UseUpdatedEscapeRules;
 
#nullable enable
        internal sealed override ObsoleteAttributeData? ObsoleteAttributeData
            => _underlyingModule.ObsoleteAttributeData;
    }
}