|
// 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.Collections.ObjectModel;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using System.Diagnostics;
using System.Globalization;
using System.Threading;
namespace Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting
{
/// <summary>
/// Essentially this is a wrapper around another AssemblySymbol that is responsible for retargeting
/// symbols from one assembly to another. It can retarget symbols for multiple assemblies at the same time.
///
/// For example, compilation C1 references v1 of Lib.dll and compilation C2 references C1 and v2 of Lib.dll.
/// In this case, in context of C2, all types from v1 of Lib.dll leaking through C1 (through method
/// signatures, etc.) must be retargeted to the types from v2 of Lib.dll. This is what
/// RetargetingAssemblySymbol is responsible for. In the example above, modules in C2 do not
/// reference C1.m_AssemblySymbol, but reference a special RetargetingAssemblySymbol created for
/// C1 by ReferenceManager.
///
/// Here is how retargeting is implemented in general:
/// - Symbols from underlying assembly are substituted with retargeting symbols.
/// - Symbols from referenced assemblies that can be reused as is (i.e. doesn'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 class RetargetingAssemblySymbol : NonMissingAssemblySymbol
{
/// <summary>
/// The underlying AssemblySymbol, it leaks symbols that should be retargeted.
/// This cannot be an instance of RetargetingAssemblySymbol.
/// </summary>
private readonly SourceAssemblySymbol _underlyingAssembly;
/// <summary>
/// The list of contained ModuleSymbol objects. First item in the list
/// is RetargetingModuleSymbol that wraps corresponding SourceModuleSymbol
/// from underlyingAssembly.Modules list, the rest are PEModuleSymbols for
/// added modules.
/// </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>
/// Backing field for the map from a local NoPia type to corresponding canonical type.
/// </summary>
private ConcurrentDictionary<NamedTypeSymbol, NamedTypeSymbol> _noPiaUnificationMap;
/// <summary>
/// A map from a local NoPia type to corresponding canonical type.
/// </summary>
internal ConcurrentDictionary<NamedTypeSymbol, NamedTypeSymbol> NoPiaUnificationMap =>
LazyInitializer.EnsureInitialized(ref _noPiaUnificationMap, () => new ConcurrentDictionary<NamedTypeSymbol, NamedTypeSymbol>(concurrencyLevel: 2, capacity: 0));
/// <summary>
/// Assembly is /l-ed by compilation that is using it as a reference.
/// </summary>
private readonly bool _isLinked;
/// <summary>
/// Retargeted custom attributes
/// </summary>
private ImmutableArray<CSharpAttributeData> _lazyCustomAttributes;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="underlyingAssembly">
/// The underlying AssemblySymbol, cannot be an instance of RetargetingAssemblySymbol.
/// </param>
/// <param name="isLinked">
/// Assembly is /l-ed by compilation that is using it as a reference.
/// </param>
public RetargetingAssemblySymbol(SourceAssemblySymbol underlyingAssembly, bool isLinked)
{
Debug.Assert((object)underlyingAssembly != null);
_underlyingAssembly = underlyingAssembly;
ModuleSymbol[] modules = new ModuleSymbol[underlyingAssembly.Modules.Length];
modules[0] = new RetargetingModuleSymbol(this, (SourceModuleSymbol)underlyingAssembly.Modules[0]);
for (int i = 1; i < underlyingAssembly.Modules.Length; i++)
{
PEModuleSymbol under = (PEModuleSymbol)underlyingAssembly.Modules[i];
modules[i] = new PEModuleSymbol(this, under.Module, under.ImportOptions, i);
}
_modules = modules.AsImmutableOrNull();
_isLinked = isLinked;
}
private RetargetingModuleSymbol.RetargetingSymbolTranslator RetargetingTranslator
{
get
{
return ((RetargetingModuleSymbol)_modules[0]).RetargetingTranslator;
}
}
/// <summary>
/// The underlying <see cref="SourceAssemblySymbol"/>.
/// </summary>
public SourceAssemblySymbol UnderlyingAssembly
{
get
{
return _underlyingAssembly;
}
}
public override bool IsImplicitlyDeclared
{
get { return _underlyingAssembly.IsImplicitlyDeclared; }
}
public override AssemblyIdentity Identity
{
get
{
return _underlyingAssembly.Identity;
}
}
public override Version AssemblyVersionPattern => _underlyingAssembly.AssemblyVersionPattern;
internal override ImmutableArray<byte> PublicKey
{
get { return _underlyingAssembly.PublicKey; }
}
public override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken))
{
return _underlyingAssembly.GetDocumentationCommentXml(preferredCulture, expandIncludes, cancellationToken);
}
public override ImmutableArray<ModuleSymbol> Modules
{
get
{
return _modules;
}
}
internal override bool KeepLookingForDeclaredSpecialTypes
{
get
{
// RetargetingAssemblySymbol never represents Core library.
return false;
}
}
public override ImmutableArray<Location> Locations
{
get
{
return _underlyingAssembly.Locations;
}
}
internal override bool HasImportedFromTypeLibAttribute => _underlyingAssembly.HasImportedFromTypeLibAttribute;
internal override bool HasPrimaryInteropAssemblyAttribute => _underlyingAssembly.HasPrimaryInteropAssemblyAttribute;
internal override IEnumerable<ImmutableArray<byte>> GetInternalsVisibleToPublicKeys(string simpleName)
{
return _underlyingAssembly.GetInternalsVisibleToPublicKeys(simpleName);
}
internal override IEnumerable<string> GetInternalsVisibleToAssemblyNames()
{
return _underlyingAssembly.GetInternalsVisibleToAssemblyNames();
}
internal override bool AreInternalsVisibleToThisAssembly(AssemblySymbol other)
{
return _underlyingAssembly.AreInternalsVisibleToThisAssembly(other);
}
public override ImmutableArray<CSharpAttributeData> GetAttributes()
{
return RetargetingTranslator.GetRetargetedAttributes(_underlyingAssembly.GetAttributes(), ref _lazyCustomAttributes);
}
/// <summary>
/// Lookup declaration for FX type in this Assembly.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
/// <remarks></remarks>
internal override NamedTypeSymbol GetDeclaredSpecialType(ExtendedSpecialType type)
{
// Cor library should not have any references and, therefore, should never be
// wrapped by a RetargetingAssemblySymbol.
throw ExceptionUtilities.Unreachable();
}
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 bool IsLinked
{
get
{
return _isLinked;
}
}
public override ICollection<string> TypeNames
{
get
{
return _underlyingAssembly.TypeNames;
}
}
public override ICollection<string> NamespaceNames
{
get
{
return _underlyingAssembly.NamespaceNames;
}
}
public override bool MightContainExtensionMethods
{
get
{
return _underlyingAssembly.MightContainExtensionMethods;
}
}
internal sealed override CSharpCompilation DeclaringCompilation // perf, not correctness
{
get { return null; }
}
internal override TypeConversions TypeConversions => CorLibrary.TypeConversions;
internal override bool GetGuidString(out string guidString)
{
return _underlyingAssembly.GetGuidString(out guidString);
}
#nullable enable
internal sealed override ObsoleteAttributeData? ObsoleteAttributeData
=> _underlyingAssembly.ObsoleteAttributeData;
internal override NamedTypeSymbol? TryLookupForwardedMetadataTypeWithCycleDetection(ref MetadataTypeName emittedName, ConsList<AssemblySymbol>? visitedAssemblies)
{
NamedTypeSymbol? underlying = _underlyingAssembly.TryLookupForwardedMetadataTypeWithCycleDetection(ref emittedName, visitedAssemblies: null);
if ((object?)underlying == null)
{
return null;
}
return this.RetargetingTranslator.Retarget(underlying, RetargetOptions.RetargetPrimitiveTypesByName);
}
#nullable disable
internal override IEnumerable<NamedTypeSymbol> GetAllTopLevelForwardedTypes()
{
foreach (NamedTypeSymbol underlying in _underlyingAssembly.GetAllTopLevelForwardedTypes())
{
yield return this.RetargetingTranslator.Retarget(underlying, RetargetOptions.RetargetPrimitiveTypesByName);
}
}
public override AssemblyMetadata GetMetadata() => _underlyingAssembly.GetMetadata();
}
}
|