|
// 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.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Reflection.PortableExecutable;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
/// <summary>
/// Represents a .NET assembly, consisting of one or more modules.
/// </summary>
internal abstract class AssemblySymbol : Symbol, IAssemblySymbolInternal
{
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Changes to the public interface of this class should remain synchronized with the VB version.
// Do not make any changes to the public interface without making the corresponding change
// to the VB version.
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
/// <summary>
/// The system assembly, which provides primitive types like Object, String, etc., e.g. mscorlib.dll.
/// The value is provided by ReferenceManager and must not be modified. For SourceAssemblySymbol, non-missing
/// coreLibrary must match one of the referenced assemblies returned by GetReferencedAssemblySymbols() method of
/// the main module. If there is no existing assembly that can be used as a source for the primitive types,
/// the value is a Compilation.MissingCorLibrary.
/// </summary>
private AssemblySymbol _corLibrary;
/// <summary>
/// The system assembly, which provides primitive types like Object, String, etc., e.g. mscorlib.dll.
/// The value is MissingAssemblySymbol if none of the referenced assemblies can be used as a source for the
/// primitive types and the owning assembly cannot be used as the source too. Otherwise, it is one of
/// the referenced assemblies returned by GetReferencedAssemblySymbols() method or the owning assembly.
/// </summary>
internal AssemblySymbol CorLibrary
{
get
{
return _corLibrary;
}
}
internal abstract TypeConversions TypeConversions { get; }
/// <summary>
/// A helper method for ReferenceManager to set the system assembly, which provides primitive
/// types like Object, String, etc., e.g. mscorlib.dll.
/// </summary>
internal void SetCorLibrary(AssemblySymbol corLibrary)
{
Debug.Assert((object)_corLibrary == null);
_corLibrary = corLibrary;
}
/// <summary>
/// Simple name the assembly.
/// </summary>
/// <remarks>
/// This is equivalent to <see cref="Identity"/>.<see cref="AssemblyIdentity.Name"/>, but may be
/// much faster to retrieve for source code assemblies, since it does not require binding
/// the assembly-level attributes that contain the version number and other assembly
/// information.
/// </remarks>
public override string Name
{
get
{
return Identity.Name;
}
}
/// <summary>
/// Gets the identity of this assembly.
/// </summary>
public abstract AssemblyIdentity Identity { get; }
AssemblyIdentity IAssemblySymbolInternal.Identity => Identity;
IAssemblySymbolInternal IAssemblySymbolInternal.CorLibrary => CorLibrary;
/// <summary>
/// Assembly version pattern with wildcards represented by <see cref="ushort.MaxValue"/>,
/// or null if the version string specified in the <see cref="AssemblyVersionAttribute"/> doesn't contain a wildcard.
///
/// For example,
/// AssemblyVersion("1.2.*") is represented as 1.2.65535.65535,
/// AssemblyVersion("1.2.3.*") is represented as 1.2.3.65535.
/// </summary>
public abstract Version AssemblyVersionPattern { get; }
/// <summary>
/// Target architecture of the machine.
/// </summary>
internal Machine Machine
{
get
{
return Modules[0].Machine;
}
}
/// <summary>
/// Indicates that this PE file makes Win32 calls. See CorPEKind.pe32BitRequired for more information (http://msdn.microsoft.com/en-us/library/ms230275.aspx).
/// </summary>
internal bool Bit32Required
{
get
{
return Modules[0].Bit32Required;
}
}
/// <summary>
/// Gets the merged root namespace that contains all namespaces and types defined in the modules
/// of this assembly. If there is just one module in this assembly, this property just returns the
/// GlobalNamespace of that module.
/// </summary>
public abstract NamespaceSymbol GlobalNamespace
{
get;
}
/// <summary>
/// Given a namespace symbol, returns the corresponding assembly specific namespace symbol
/// </summary>
internal NamespaceSymbol GetAssemblyNamespace(NamespaceSymbol namespaceSymbol)
{
if (namespaceSymbol.IsGlobalNamespace)
{
return this.GlobalNamespace;
}
NamespaceSymbol container = namespaceSymbol.ContainingNamespace;
if ((object)container == null)
{
return this.GlobalNamespace;
}
if (namespaceSymbol.NamespaceKind == NamespaceKind.Assembly && namespaceSymbol.ContainingAssembly == this)
{
// this is already the correct assembly namespace
return namespaceSymbol;
}
NamespaceSymbol assemblyContainer = GetAssemblyNamespace(container);
if ((object)assemblyContainer == (object)container)
{
// Trivial case, container isn't merged.
return namespaceSymbol;
}
if ((object)assemblyContainer == null)
{
return null;
}
return assemblyContainer.GetNestedNamespace(namespaceSymbol.Name);
}
/// <summary>
/// Gets a read-only list of all the modules in this assembly. (There must be at least one.) The first one is the main module
/// that holds the assembly manifest.
/// </summary>
public abstract ImmutableArray<ModuleSymbol> Modules { get; }
internal override TResult Accept<TArgument, TResult>(CSharpSymbolVisitor<TArgument, TResult> visitor, TArgument argument)
{
return visitor.VisitAssembly(this, argument);
}
public override void Accept(CSharpSymbolVisitor visitor)
{
visitor.VisitAssembly(this);
}
public override TResult Accept<TResult>(CSharpSymbolVisitor<TResult> visitor)
{
return visitor.VisitAssembly(this);
}
public sealed override SymbolKind Kind
{
get
{
return SymbolKind.Assembly;
}
}
public sealed override AssemblySymbol ContainingAssembly
{
get
{
return null;
}
}
// Only the compiler can create AssemblySymbols.
internal AssemblySymbol()
{
}
/// <summary>
/// Does this symbol represent a missing assembly.
/// </summary>
internal abstract bool IsMissing
{
get;
}
public sealed override Accessibility DeclaredAccessibility
{
get
{
return Accessibility.NotApplicable;
}
}
public sealed override bool IsStatic
{
get
{
return false;
}
}
public sealed override bool IsVirtual
{
get
{
return false;
}
}
public sealed override bool IsOverride
{
get
{
return false;
}
}
public sealed override bool IsAbstract
{
get
{
return false;
}
}
public sealed override bool IsSealed
{
get
{
return false;
}
}
public sealed override bool IsExtern
{
get
{
return false;
}
}
public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences
{
get
{
return ImmutableArray<SyntaxReference>.Empty;
}
}
/// <summary>
/// True if the assembly contains interactive code.
/// </summary>
public virtual bool IsInteractive
{
get
{
return false;
}
}
public sealed override Symbol ContainingSymbol
{
get
{
return null;
}
}
internal abstract bool HasImportedFromTypeLibAttribute { get; }
internal abstract bool HasPrimaryInteropAssemblyAttribute { get; }
#nullable enable
/// <summary>
/// Lookup a top level type referenced from metadata, names should be
/// compared case-sensitively.
/// </summary>
/// <param name="emittedName">
/// Full type name with generic name mangling.
/// </param>
/// <remarks></remarks>
/// <returns>The symbol for the type declared in this assembly, or null.</returns>
internal abstract NamedTypeSymbol? LookupDeclaredTopLevelMetadataType(ref MetadataTypeName emittedName);
/// <summary>
/// Lookup a top level type referenced from metadata, names should be
/// compared case-sensitively. Detect cycles during lookup.
/// </summary>
/// <param name="emittedName">
/// Full type name, possibly with generic name mangling.
/// </param>
/// <param name="visitedAssemblies">
/// List of assemblies lookup has already visited (since type forwarding can introduce cycles).
/// </param>
internal abstract NamedTypeSymbol LookupDeclaredOrForwardedTopLevelMetadataType(ref MetadataTypeName emittedName, ConsList<AssemblySymbol>? visitedAssemblies);
/// <summary>
/// Returns the type symbol for a forwarded type based on its canonical CLR metadata name.
/// The name should refer to a non-nested type. If type with this name is not forwarded,
/// null is returned.
/// </summary>
public NamedTypeSymbol? ResolveForwardedType(string fullyQualifiedMetadataName)
{
if (fullyQualifiedMetadataName == null)
{
throw new ArgumentNullException(nameof(fullyQualifiedMetadataName));
}
var emittedName = MetadataTypeName.FromFullName(fullyQualifiedMetadataName);
return TryLookupForwardedMetadataTypeWithCycleDetection(ref emittedName, visitedAssemblies: null);
}
/// <summary>
/// Look up the given metadata type, if it is forwarded.
/// </summary>
internal virtual NamedTypeSymbol? TryLookupForwardedMetadataTypeWithCycleDetection(ref MetadataTypeName emittedName, ConsList<AssemblySymbol>? visitedAssemblies)
{
return null;
}
internal ErrorTypeSymbol CreateCycleInTypeForwarderErrorTypeSymbol(ref MetadataTypeName emittedName)
{
DiagnosticInfo diagnosticInfo = new CSDiagnosticInfo(ErrorCode.ERR_CycleInTypeForwarder, emittedName.FullName, this.Name);
return new MissingMetadataTypeSymbol.TopLevel(this.Modules[0], ref emittedName, diagnosticInfo);
}
internal ErrorTypeSymbol CreateMultipleForwardingErrorTypeSymbol(ref MetadataTypeName emittedName, ModuleSymbol forwardingModule, AssemblySymbol destination1, AssemblySymbol destination2)
{
var diagnosticInfo = new CSDiagnosticInfo(ErrorCode.ERR_TypeForwardedToMultipleAssemblies, forwardingModule, this, emittedName.FullName, destination1, destination2);
return new MissingMetadataTypeSymbol.TopLevel(forwardingModule, ref emittedName, diagnosticInfo);
}
internal abstract IEnumerable<NamedTypeSymbol> GetAllTopLevelForwardedTypes();
#nullable disable
/// <summary>
/// Lookup declaration for predefined CorLib type in this Assembly.
/// </summary>
/// <returns>The symbol for the pre-defined type or an error type if the type is not defined in the core library.</returns>
internal abstract NamedTypeSymbol GetDeclaredSpecialType(ExtendedSpecialType type);
/// <summary>
/// Register declaration of predefined CorLib type in this Assembly.
/// </summary>
/// <param name="corType"></param>
internal virtual void RegisterDeclaredSpecialType(NamedTypeSymbol corType)
{
throw ExceptionUtilities.Unreachable();
}
/// <summary>
/// Continue looking for declaration of predefined CorLib type in this Assembly
/// while symbols for new type declarations are constructed.
/// </summary>
internal virtual bool KeepLookingForDeclaredSpecialTypes
{
get
{
throw ExceptionUtilities.Unreachable();
}
}
/// <summary>
/// Return the native integer type corresponding to the underlying type.
/// </summary>
internal virtual NamedTypeSymbol GetNativeIntegerType(NamedTypeSymbol underlyingType)
{
throw ExceptionUtilities.Unreachable();
}
public bool SupportsRuntimeCapability(RuntimeCapability capability)
{
// Keep in sync with VB's AssemblySymbol.SupportsRuntimeCapability
switch (capability)
{
case RuntimeCapability.ByRefFields:
return this.RuntimeSupportsByRefFields;
case RuntimeCapability.CovariantReturnsOfClasses:
return this.RuntimeSupportsCovariantReturnsOfClasses;
case RuntimeCapability.DefaultImplementationsOfInterfaces:
return this.RuntimeSupportsDefaultInterfaceImplementation;
case RuntimeCapability.NumericIntPtr:
return this.RuntimeSupportsNumericIntPtr;
case RuntimeCapability.UnmanagedSignatureCallingConvention:
return this.RuntimeSupportsUnmanagedSignatureCallingConvention;
case RuntimeCapability.VirtualStaticsInInterfaces:
return this.RuntimeSupportsStaticAbstractMembersInInterfaces;
case RuntimeCapability.InlineArrayTypes:
return this.RuntimeSupportsInlineArrayTypes;
case RuntimeCapability.ByRefLikeGenerics:
return this.RuntimeSupportsByRefLikeGenerics;
}
return false;
}
/// <summary>
/// Figure out if the target runtime supports default interface implementation.
/// </summary>
internal bool RuntimeSupportsDefaultInterfaceImplementation
{
// Keep in sync with VB's AssemblySymbol.RuntimeSupportsDefaultInterfaceImplementation
get => RuntimeSupportsFeature(SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__DefaultImplementationsOfInterfaces);
}
/// <summary>
/// Figure out if the target runtime supports static abstract members in interfaces.
/// </summary>
internal bool RuntimeSupportsStaticAbstractMembersInInterfaces
{
// Keep in sync with VB's AssemblySymbol.RuntimeSupportsVirtualStaticsInInterfaces
get => RuntimeSupportsFeature(SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__VirtualStaticsInInterfaces);
}
/// <summary>
/// Whether the target runtime supports numeric IntPtr types.
/// </summary>
internal bool RuntimeSupportsNumericIntPtr
{
// Keep in sync with VB's AssemblySymbol.RuntimeSupportsNumericIntPtr
get
{
// CorLibrary should never be null, but that invariant is broken in some cases for MissingAssemblySymbol.
// Tracked by https://github.com/dotnet/roslyn/issues/61262
return CorLibrary is not null &&
RuntimeSupportsFeature(SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__NumericIntPtr);
}
}
/// <summary>
/// Figure out if the target runtime supports inline array types.
/// </summary>
internal bool RuntimeSupportsInlineArrayTypes
{
// Keep in sync with VB's AssemblySymbol.RuntimeSupportsInlineArrayTypes
get
{
return GetSpecialTypeMember(SpecialMember.System_Runtime_CompilerServices_InlineArrayAttribute__ctor) is object;
}
}
/// <summary>
/// Figure out if the target runtime supports inline array types.
/// </summary>
internal bool RuntimeSupportsByRefLikeGenerics
{
// Keep in sync with VB's AssemblySymbol.RuntimeSupportsByRefLikeGenerics
get
{
// CorLibrary should never be null, but that invariant is broken in some cases for MissingAssemblySymbol.
// Tracked by https://github.com/dotnet/roslyn/issues/61262
return CorLibrary is not null &&
RuntimeSupportsFeature(SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__ByRefLikeGenerics);
}
}
protected bool RuntimeSupportsFeature(SpecialMember feature)
{
// Keep in sync with VB's AssemblySymbol.RuntimeSupportsFeature
Debug.Assert(SpecialMembers.GetDescriptor(feature).DeclaringSpecialType == SpecialType.System_Runtime_CompilerServices_RuntimeFeature);
return GetSpecialType(SpecialType.System_Runtime_CompilerServices_RuntimeFeature) is { TypeKind: TypeKind.Class, IsStatic: true } &&
GetSpecialTypeMember(feature) is object;
}
// Keep in sync with VB's AssemblySymbol.RuntimeSupportsUnmanagedSignatureCallingConvention
internal bool RuntimeSupportsUnmanagedSignatureCallingConvention
=> RuntimeSupportsFeature(SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__UnmanagedSignatureCallingConvention);
// Keep in sync with VB's AssemblySymbol.RuntimeSupportsByRefFields
internal bool RuntimeSupportsByRefFields
=> RuntimeSupportsFeature(SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__ByRefFields);
/// <summary>
/// True if the target runtime support covariant returns of methods declared in classes.
/// </summary>
internal bool RuntimeSupportsCovariantReturnsOfClasses
{
// Keep in sync with VB's AssemblySymbol.RuntimeSupportsCovariantReturnsOfClasses
get
{
// check for the runtime feature indicator and the required attribute.
return
RuntimeSupportsFeature(SpecialMember.System_Runtime_CompilerServices_RuntimeFeature__CovariantReturnsOfClasses) &&
GetSpecialType(SpecialType.System_Runtime_CompilerServices_PreserveBaseOverridesAttribute) is { TypeKind: TypeKind.Class };
}
}
/// <summary>
/// Return an array of assemblies involved in canonical type resolution of
/// NoPia local types defined within this assembly. In other words, all
/// references used by previous compilation referencing this assembly.
/// </summary>
/// <returns></returns>
internal abstract ImmutableArray<AssemblySymbol> GetNoPiaResolutionAssemblies();
internal abstract void SetNoPiaResolutionAssemblies(ImmutableArray<AssemblySymbol> assemblies);
/// <summary>
/// Return 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.
/// </summary>
internal abstract ImmutableArray<AssemblySymbol> GetLinkedReferencedAssemblies();
internal abstract void SetLinkedReferencedAssemblies(ImmutableArray<AssemblySymbol> assemblies);
IEnumerable<ImmutableArray<byte>> IAssemblySymbolInternal.GetInternalsVisibleToPublicKeys(string simpleName)
=> GetInternalsVisibleToPublicKeys(simpleName);
internal abstract IEnumerable<ImmutableArray<byte>> GetInternalsVisibleToPublicKeys(string simpleName);
IEnumerable<string> IAssemblySymbolInternal.GetInternalsVisibleToAssemblyNames()
=> GetInternalsVisibleToAssemblyNames();
internal abstract IEnumerable<string> GetInternalsVisibleToAssemblyNames();
bool IAssemblySymbolInternal.AreInternalsVisibleToThisAssembly(IAssemblySymbolInternal otherAssembly)
=> AreInternalsVisibleToThisAssembly((AssemblySymbol)otherAssembly);
internal abstract bool AreInternalsVisibleToThisAssembly(AssemblySymbol other);
/// <summary>
/// Assembly is /l-ed by compilation that is using it as a reference.
/// </summary>
internal abstract bool IsLinked { get; }
/// <summary>
/// Returns true and a string from the first GuidAttribute on the assembly,
/// the string might be null or an invalid guid representation. False,
/// if there is no GuidAttribute with string argument.
/// </summary>
internal abstract bool GetGuidString(out string guidString);
/// <summary>
/// Gets the set of type identifiers from this assembly.
/// </summary>
/// <remarks>
/// These names are the simple identifiers for the type, and do not include namespaces,
/// outer type names, or type parameters.
///
/// This functionality can be used for features that want to quickly know if a name could be
/// a type for performance reasons. For example, classification does not want to incur an
/// expensive binding call cost if it knows that there is no type with the name that they
/// are looking at.
/// </remarks>
public abstract ICollection<string> TypeNames { get; }
/// <summary>
/// Gets the set of namespace names from this assembly.
/// </summary>
public abstract ICollection<string> NamespaceNames { get; }
/// <summary>
/// Returns true if this assembly might contain extension methods. If this property
/// returns false, there are no extension methods in this assembly.
/// </summary>
/// <remarks>
/// This property allows the search for extension methods to be narrowed quickly.
/// </remarks>
public abstract bool MightContainExtensionMethods { get; }
/// <summary>
/// Gets the symbol for the pre-defined type from core library associated with this assembly.
/// </summary>
/// <returns>The symbol for the pre-defined type or an error type if the type is not defined in the core library.</returns>
internal NamedTypeSymbol GetSpecialType(ExtendedSpecialType type)
{
return CorLibrary.GetDeclaredSpecialType(type);
}
internal static TypeSymbol DynamicType
{
get
{
return DynamicTypeSymbol.Instance;
}
}
/// <summary>
/// The NamedTypeSymbol for the .NET System.Object type, which could have a TypeKind of
/// Error if there was no COR Library in a compilation using the assembly.
/// </summary>
internal NamedTypeSymbol ObjectType
{
get
{
return GetSpecialType(SpecialType.System_Object);
}
}
/// <summary>
/// Get symbol for predefined type from Cor Library used by this assembly.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
internal NamedTypeSymbol GetPrimitiveType(Microsoft.Cci.PrimitiveTypeCode type)
{
return GetSpecialType(SpecialTypes.GetTypeFromMetadataName(type));
}
#nullable enable
/// <summary>
/// Lookup a type within the assembly using the canonical CLR metadata name of the type.
/// </summary>
/// <param name="fullyQualifiedMetadataName">Type name.</param>
/// <returns>Symbol for the type or null if type cannot be found or is ambiguous. </returns>
public NamedTypeSymbol? GetTypeByMetadataName(string fullyQualifiedMetadataName)
{
if (fullyQualifiedMetadataName == null)
{
throw new ArgumentNullException(nameof(fullyQualifiedMetadataName));
}
var result = this.GetTypeByMetadataName(fullyQualifiedMetadataName, includeReferences: false, isWellKnownType: false, conflicts: out var _);
Debug.Assert(result?.IsErrorType() != true);
return result;
}
/// <summary>
/// Lookup a type within the assembly using its canonical CLR metadata name.
/// </summary>
/// <param name="metadataName"></param>
/// <param name="includeReferences">
/// If search within assembly fails, lookup in assemblies referenced by the primary module.
/// For source assembly, this is equivalent to all assembly references given to compilation.
/// </param>
/// <param name="isWellKnownType">
/// Extra restrictions apply when searching for a well-known type. In particular, the type must be public.
/// </param>
/// <param name="useCLSCompliantNameArityEncoding">
/// While resolving the name, consider only types following CLS-compliant generic type names and arity encoding (ECMA-335, section 10.7.2).
/// I.e. arity is inferred from the name and matching type must have the same emitted name and arity.
/// </param>
/// <param name="warnings">
/// A diagnostic bag to receive warnings if we should allow multiple definitions and pick one.
/// </param>
/// <param name="ignoreCorLibraryDuplicatedTypes">
/// In case duplicate types are found, ignore the one from corlib. This is useful for any kind of compilation at runtime
/// (EE/scripting/Powershell) using a type that is being migrated to corlib.
/// </param>
/// <param name="conflicts">
/// In cases a type could not be found because of ambiguity, we return two of the candidates that caused the ambiguity.
/// </param>
/// <returns>Null if the type can't be found.</returns>
internal NamedTypeSymbol? GetTypeByMetadataName(
string metadataName,
bool includeReferences,
bool isWellKnownType,
out (AssemblySymbol, AssemblySymbol) conflicts,
bool useCLSCompliantNameArityEncoding = false,
DiagnosticBag? warnings = null,
bool ignoreCorLibraryDuplicatedTypes = false)
{
NamedTypeSymbol? type;
MetadataTypeName mdName;
if (metadataName.IndexOf('+') >= 0)
{
var parts = metadataName.Split(s_nestedTypeNameSeparators);
Debug.Assert(parts.Length > 0);
mdName = MetadataTypeName.FromFullName(parts[0], useCLSCompliantNameArityEncoding);
type = GetTopLevelTypeByMetadataName(ref mdName, assemblyOpt: null, includeReferences: includeReferences, isWellKnownType: isWellKnownType,
conflicts: out conflicts, warnings: warnings, ignoreCorLibraryDuplicatedTypes: ignoreCorLibraryDuplicatedTypes);
if (type is null)
{
return null;
}
Debug.Assert(!type.IsErrorType());
for (int i = 1; i < parts.Length; i++)
{
mdName = MetadataTypeName.FromTypeName(parts[i]);
type = type.LookupMetadataType(ref mdName);
if (type is null)
{
return null;
}
Debug.Assert(!type.IsErrorType());
if (isWellKnownType && !IsValidWellKnownType(type))
{
return null;
}
}
}
else
{
mdName = MetadataTypeName.FromFullName(metadataName, useCLSCompliantNameArityEncoding);
type = GetTopLevelTypeByMetadataName(ref mdName, assemblyOpt: null, includeReferences: includeReferences, isWellKnownType: isWellKnownType,
conflicts: out conflicts, warnings: warnings, ignoreCorLibraryDuplicatedTypes: ignoreCorLibraryDuplicatedTypes);
}
Debug.Assert(type?.IsErrorType() != true);
return type;
}
private static readonly char[] s_nestedTypeNameSeparators = new char[] { '+' };
/// <summary>
/// Resolves <see cref="System.Type"/> to a <see cref="TypeSymbol"/> available in this assembly
/// its referenced assemblies.
/// </summary>
/// <param name="type">The type to resolve.</param>
/// <returns>The resolved symbol if successful or null on failure.</returns>
internal TypeSymbol? GetTypeByReflectionType(Type type)
{
System.Reflection.TypeInfo typeInfo = type.GetTypeInfo();
Debug.Assert(!typeInfo.IsByRef);
// not supported (we don't accept open types as submission results nor host types):
Debug.Assert(!typeInfo.ContainsGenericParameters);
if (typeInfo.IsArray)
{
TypeSymbol? symbol = GetTypeByReflectionType(typeInfo.GetElementType()!);
if (symbol is null)
{
return null;
}
int rank = typeInfo.GetArrayRank();
return ArrayTypeSymbol.CreateCSharpArray(this, TypeWithAnnotations.Create(symbol), rank);
}
else if (typeInfo.IsPointer)
{
TypeSymbol? symbol = GetTypeByReflectionType(typeInfo.GetElementType()!);
if (symbol is null)
{
return null;
}
return new PointerTypeSymbol(TypeWithAnnotations.Create(symbol));
}
else if (typeInfo.DeclaringType != null)
{
Debug.Assert(!typeInfo.IsArray);
// consolidated generic arguments (includes arguments of all declaring types):
Type[] genericArguments = typeInfo.GenericTypeArguments;
int typeArgumentIndex = 0;
var currentTypeInfo = typeInfo.IsGenericType ? typeInfo.GetGenericTypeDefinition().GetTypeInfo() : typeInfo;
var nestedTypes = ArrayBuilder<System.Reflection.TypeInfo>.GetInstance();
while (true)
{
Debug.Assert(currentTypeInfo.IsGenericTypeDefinition || !currentTypeInfo.IsGenericType);
nestedTypes.Add(currentTypeInfo);
if (currentTypeInfo.DeclaringType == null)
{
break;
}
currentTypeInfo = currentTypeInfo.DeclaringType.GetTypeInfo();
}
int i = nestedTypes.Count - 1;
var symbol = (NamedTypeSymbol?)GetTypeByReflectionType(nestedTypes[i].AsType());
if (symbol is null)
{
return null;
}
while (--i >= 0)
{
int forcedArity = nestedTypes[i].GenericTypeParameters.Length - nestedTypes[i + 1].GenericTypeParameters.Length;
MetadataTypeName mdName = MetadataTypeName.FromTypeName(nestedTypes[i].Name, forcedArity: forcedArity);
symbol = symbol.LookupMetadataType(ref mdName);
Debug.Assert(symbol?.IsErrorType() != true);
if (symbol is null)
{
return null;
}
symbol = ApplyGenericArguments(symbol, genericArguments, ref typeArgumentIndex);
if (symbol is null)
{
return null;
}
}
nestedTypes.Free();
Debug.Assert(typeArgumentIndex == genericArguments.Length);
return symbol;
}
else
{
AssemblyIdentity assemblyId = AssemblyIdentity.FromAssemblyDefinition(typeInfo.Assembly);
MetadataTypeName mdName = MetadataTypeName.FromNamespaceAndTypeName(
typeInfo.Namespace ?? string.Empty,
typeInfo.Name,
forcedArity: typeInfo.GenericTypeArguments.Length);
NamedTypeSymbol? symbol = GetTopLevelTypeByMetadataName(ref mdName, assemblyId, includeReferences: true, isWellKnownType: false, conflicts: out var _);
if (symbol is null)
{
return null;
}
Debug.Assert(!symbol.IsErrorType());
int typeArgumentIndex = 0;
Type[] genericArguments = typeInfo.GenericTypeArguments;
symbol = ApplyGenericArguments(symbol, genericArguments, ref typeArgumentIndex);
Debug.Assert(typeArgumentIndex == genericArguments.Length);
return symbol;
}
}
private NamedTypeSymbol? ApplyGenericArguments(NamedTypeSymbol symbol, Type[] typeArguments, ref int currentTypeArgument)
{
int remainingTypeArguments = typeArguments.Length - currentTypeArgument;
// in case we are specializing a nested generic definition we might have more arguments than the current symbol:
Debug.Assert(remainingTypeArguments >= symbol.Arity);
if (remainingTypeArguments == 0)
{
return symbol;
}
var length = symbol.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics.Length;
var typeArgumentSymbols = ArrayBuilder<TypeWithAnnotations>.GetInstance(length);
for (int i = 0; i < length; i++)
{
var argSymbol = GetTypeByReflectionType(typeArguments[currentTypeArgument++]);
if (argSymbol is null)
{
return null;
}
typeArgumentSymbols.Add(TypeWithAnnotations.Create(argSymbol));
}
return symbol.ConstructIfGeneric(typeArgumentSymbols.ToImmutableAndFree());
}
internal NamedTypeSymbol? GetTopLevelTypeByMetadataName(
ref MetadataTypeName metadataName,
AssemblyIdentity? assemblyOpt,
bool includeReferences,
bool isWellKnownType,
out (AssemblySymbol, AssemblySymbol) conflicts,
DiagnosticBag? warnings = null, // this is set to collect ambiguity warning for well-known types before C# 7
bool ignoreCorLibraryDuplicatedTypes = false)
{
// Type from this assembly always wins.
// After that we look in references, which may yield ambiguities. If `ignoreCorLibraryDuplicatedTypes` is set,
// corlib does not contribute to ambiguities (corlib loses over other references).
// For well-known types before C# 7, ambiguities are reported as a warning and the first candidate wins.
// For other types, when `ignoreCorLibraryDuplicatedTypes` isn't set, finding a candidate in corlib resolves
// ambiguities (corlib wins over other references).
Debug.Assert(warnings is null || isWellKnownType);
conflicts = default;
NamedTypeSymbol? result;
// First try this assembly
result = GetTopLevelTypeByMetadataName(this, ref metadataName, assemblyOpt);
Debug.Assert(result?.IsErrorType() != true);
if (isWellKnownType && !IsValidWellKnownType(result))
{
result = null;
}
// ignore any types of the same name that might be in referenced assemblies (prefer the current assembly):
if (result is object || !includeReferences)
{
return result;
}
// Then try corlib, when finding a result there means we've found the final result
bool isWellKnownTypeBeforeCSharp7 = isWellKnownType && warnings is not null;
bool skipCorLibrary = false;
if (CorLibrary != (object)this &&
!CorLibrary.IsMissing &&
!isWellKnownTypeBeforeCSharp7 && !ignoreCorLibraryDuplicatedTypes)
{
NamedTypeSymbol? corLibCandidate = GetTopLevelTypeByMetadataName(CorLibrary, ref metadataName, assemblyOpt);
Debug.Assert(corLibCandidate?.IsErrorType() != true);
skipCorLibrary = true;
if (isValidCandidate(corLibCandidate, isWellKnownType))
{
return corLibCandidate;
}
}
Debug.Assert(this is SourceAssemblySymbol,
"Never include references for a non-source assembly, because they don't know about aliases.");
var assemblies = ArrayBuilder<AssemblySymbol>.GetInstance();
// ignore reference aliases if searching for a type from a specific assembly:
if (assemblyOpt != null)
{
assemblies.AddRange(DeclaringCompilation.GetBoundReferenceManager().ReferencedAssemblies);
}
else
{
DeclaringCompilation.GetUnaliasedReferencedAssemblies(assemblies);
}
// Lookup in references
foreach (var assembly in assemblies)
{
Debug.Assert(!(this is SourceAssemblySymbol && assembly.IsMissing)); // Non-source assemblies can have missing references
if (skipCorLibrary && assembly == (object)CorLibrary)
{
continue;
}
NamedTypeSymbol? candidate = GetTopLevelTypeByMetadataName(assembly, ref metadataName, assemblyOpt);
Debug.Assert(candidate?.IsErrorType() != true);
if (!isValidCandidate(candidate, isWellKnownType))
{
continue;
}
Debug.Assert(!TypeSymbol.Equals(candidate, result, TypeCompareKind.ConsiderEverything));
if (result is object)
{
// duplicate
if (ignoreCorLibraryDuplicatedTypes)
{
if (IsInCorLib(candidate))
{
// ignore candidate
continue;
}
if (IsInCorLib(result))
{
// drop previous result
result = candidate;
continue;
}
}
if (warnings is null)
{
conflicts = (result.ContainingAssembly, candidate.ContainingAssembly);
result = null;
}
else
{
// The predefined type '{0}' is defined in multiple assemblies in the global alias; using definition from '{1}'
warnings.Add(ErrorCode.WRN_MultiplePredefTypes, NoLocation.Singleton, result, result.ContainingAssembly);
}
break;
}
result = candidate;
}
assemblies.Free();
Debug.Assert(result?.IsErrorType() != true);
return result;
bool isValidCandidate([NotNullWhen(true)] NamedTypeSymbol? candidate, bool isWellKnownType)
{
return candidate is not null
&& (!isWellKnownType || IsValidWellKnownType(candidate))
&& !candidate.IsHiddenByCodeAnalysisEmbeddedAttribute();
}
}
private bool IsInCorLib(NamedTypeSymbol type)
{
return (object)type.ContainingAssembly == CorLibrary;
}
private bool IsValidWellKnownType(NamedTypeSymbol? result)
{
if (result is null || result.TypeKind == TypeKind.Error)
{
return false;
}
Debug.Assert((object)result.ContainingType == null || IsValidWellKnownType(result.ContainingType),
"Checking the containing type is the caller's responsibility.");
return result.DeclaredAccessibility == Accessibility.Public || IsSymbolAccessible(result, this);
}
private static NamedTypeSymbol? GetTopLevelTypeByMetadataName(AssemblySymbol assembly, ref MetadataTypeName metadataName, AssemblyIdentity? assemblyOpt)
{
if (assemblyOpt != null && !assemblyOpt.Equals(assembly.Identity))
{
return null;
}
var result = assembly.LookupDeclaredTopLevelMetadataType(ref metadataName);
Debug.Assert(result?.IsErrorType() != true);
Debug.Assert(result is null || ReferenceEquals(result.ContainingAssembly, assembly));
return result;
}
#nullable disable
/// <summary>
/// Lookup member declaration in predefined CorLib type in this Assembly. Only valid if this
/// assembly is the Cor Library
/// </summary>
internal virtual Symbol GetDeclaredSpecialTypeMember(SpecialMember member)
{
return null;
}
/// <summary>
/// Lookup member declaration in predefined CorLib type used by this Assembly.
/// </summary>
internal virtual Symbol GetSpecialTypeMember(SpecialMember member)
{
return CorLibrary.GetDeclaredSpecialTypeMember(member);
}
internal abstract ImmutableArray<byte> PublicKey { get; }
/// <summary>
/// If this symbol represents a metadata assembly returns the underlying <see cref="AssemblyMetadata"/>.
///
/// Otherwise, this returns <see langword="null"/>.
/// </summary>
public abstract AssemblyMetadata GetMetadata();
protected override ISymbol CreateISymbol()
{
return new PublicModel.NonSourceAssemblySymbol(this);
}
}
}
|