|
// 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.Globalization;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.DocumentationComments;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE
{
/// <summary>
/// The class to represent all types imported from a PE/module.
/// </summary>
internal abstract class PENamedTypeSymbol : NamedTypeSymbol
{
private static readonly Dictionary<ReadOnlyMemory<char>, ImmutableArray<PENamedTypeSymbol>> s_emptyNestedTypes =
new Dictionary<ReadOnlyMemory<char>, ImmutableArray<PENamedTypeSymbol>>(EmptyReadOnlyMemoryOfCharComparer.Instance);
private readonly NamespaceOrTypeSymbol _container;
private readonly TypeDefinitionHandle _handle;
private readonly string _name;
private readonly TypeAttributes _flags;
private readonly ExtendedSpecialType _corTypeId;
/// <summary>
/// A set of all the names of the members in this type.
/// We can get names without getting members (which is a more expensive operation)
/// </summary>
private ICollection<string> _lazyMemberNames;
/// <summary>
/// We used to sort symbols on demand and relied on row ids to figure out the order between symbols of the same kind.
/// However, that was fragile because, when map tables are used in metadata, row ids in the map table define the order
/// and we don't have them.
/// Members are grouped by kind. First we store fields, then methods, then properties, then events and finally nested types.
/// Within groups, members are sorted based on declaration order.
/// </summary>
private ImmutableArray<Symbol> _lazyMembersInDeclarationOrder;
/// <summary>
/// A map of members immediately contained within this type
/// grouped by their name (case-sensitively).
/// </summary>
private Dictionary<string, ImmutableArray<Symbol>> _lazyMembersByName;
/// <summary>
/// A map of types immediately contained within this type
/// grouped by their name (case-sensitively).
/// </summary>
private Dictionary<ReadOnlyMemory<char>, ImmutableArray<PENamedTypeSymbol>> _lazyNestedTypes;
/// <summary>
/// Lazily initialized by TypeKind property.
/// </summary>
private TypeKind _lazyKind;
private NullableContextKind _lazyNullableContextValue;
private NamedTypeSymbol _lazyBaseType = ErrorTypeSymbol.UnknownResultType;
private ImmutableArray<NamedTypeSymbol> _lazyInterfaces = default(ImmutableArray<NamedTypeSymbol>);
private NamedTypeSymbol _lazyDeclaredBaseType = ErrorTypeSymbol.UnknownResultType;
private ImmutableArray<NamedTypeSymbol> _lazyDeclaredInterfaces = default(ImmutableArray<NamedTypeSymbol>);
private Tuple<CultureInfo, string> _lazyDocComment;
private CachedUseSiteInfo<AssemblySymbol> _lazyCachedUseSiteInfo = CachedUseSiteInfo<AssemblySymbol>.Uninitialized;
// There is a bunch of type properties relevant only for enums or types with custom attributes.
// It is fairly easy to check whether a type s is not "uncommon". So we store uncommon properties in
// a separate class with a noUncommonProperties singleton used for cases when type is "common".
// this is done purely to save memory with expectation that "uncommon" cases are indeed uncommon.
#region "Uncommon properties"
private static readonly UncommonProperties s_noUncommonProperties = new UncommonProperties();
private UncommonProperties _lazyUncommonProperties;
private UncommonProperties GetUncommonProperties()
{
var result = _lazyUncommonProperties;
if (result != null)
{
#if DEBUG
Debug.Assert(result != s_noUncommonProperties || result.IsDefaultValue(), "default value was modified");
#endif
return result;
}
if (this.IsUncommon())
{
result = new UncommonProperties();
return Interlocked.CompareExchange(ref _lazyUncommonProperties, result, null) ?? result;
}
_lazyUncommonProperties = result = s_noUncommonProperties;
return result;
}
// enums and types with custom attributes are considered uncommon
private bool IsUncommon()
{
if (this.ContainingPEModule.HasAnyCustomAttributes(_handle))
{
return true;
}
if (this.TypeKind == TypeKind.Enum)
{
return true;
}
return false;
}
private sealed class UncommonProperties
{
/// <summary>
/// Need to import them for an enum from a linked assembly, when we are embedding it. These symbols are not included into lazyMembersInDeclarationOrder.
/// </summary>
internal ImmutableArray<PEFieldSymbol> lazyInstanceEnumFields;
internal NamedTypeSymbol lazyEnumUnderlyingType;
// CONSIDER: Should we use a CustomAttributeBag for PE symbols?
internal ImmutableArray<CSharpAttributeData> lazyCustomAttributes;
internal ImmutableArray<string> lazyConditionalAttributeSymbols;
internal ObsoleteAttributeData lazyObsoleteAttributeData = ObsoleteAttributeData.Uninitialized;
internal AttributeUsageInfo lazyAttributeUsageInfo = AttributeUsageInfo.Null;
internal ThreeState lazyContainsExtensionMethods;
internal ThreeState lazyIsByRefLike;
internal ThreeState lazyIsReadOnly;
internal string lazyDefaultMemberName;
internal NamedTypeSymbol lazyComImportCoClassType = ErrorTypeSymbol.UnknownResultType;
internal CollectionBuilderAttributeData lazyCollectionBuilderAttributeData = CollectionBuilderAttributeData.Uninitialized;
internal ThreeState lazyHasEmbeddedAttribute = ThreeState.Unknown;
internal ThreeState lazyHasInterpolatedStringHandlerAttribute = ThreeState.Unknown;
internal ThreeState lazyHasRequiredMembers = ThreeState.Unknown;
internal ImmutableArray<byte> lazyFilePathChecksum = default;
internal string lazyDisplayFileName;
#if DEBUG
internal bool IsDefaultValue()
{
return lazyInstanceEnumFields.IsDefault &&
(object)lazyEnumUnderlyingType == null &&
lazyCustomAttributes.IsDefault &&
lazyConditionalAttributeSymbols.IsDefault &&
lazyObsoleteAttributeData == ObsoleteAttributeData.Uninitialized &&
lazyAttributeUsageInfo.IsNull &&
!lazyContainsExtensionMethods.HasValue() &&
lazyDefaultMemberName == null &&
(object)lazyComImportCoClassType == (object)ErrorTypeSymbol.UnknownResultType &&
!lazyHasEmbeddedAttribute.HasValue() &&
!lazyHasInterpolatedStringHandlerAttribute.HasValue() &&
!lazyHasRequiredMembers.HasValue() &&
(object)lazyCollectionBuilderAttributeData == CollectionBuilderAttributeData.Uninitialized &&
lazyFilePathChecksum.IsDefault &&
lazyDisplayFileName == null;
}
#endif
}
#endregion // Uncommon properties
internal static PENamedTypeSymbol Create(
PEModuleSymbol moduleSymbol,
PENamespaceSymbol containingNamespace,
TypeDefinitionHandle handle,
string emittedNamespaceName)
{
GenericParameterHandleCollection genericParameterHandles;
ushort arity;
BadImageFormatException mrEx = null;
GetGenericInfo(moduleSymbol, handle, out genericParameterHandles, out arity, out mrEx);
PENamedTypeSymbol result;
if (arity == 0)
{
result = new PENamedTypeSymbolNonGeneric(moduleSymbol, containingNamespace, handle, emittedNamespaceName);
}
else
{
result = new PENamedTypeSymbolGeneric(
moduleSymbol,
containingNamespace,
handle,
emittedNamespaceName,
genericParameterHandles,
arity);
}
if (mrEx != null)
{
result._lazyCachedUseSiteInfo.Initialize(result.DeriveCompilerFeatureRequiredDiagnostic() ?? new CSDiagnosticInfo(ErrorCode.ERR_BogusType, result));
}
return result;
}
private static void GetGenericInfo(PEModuleSymbol moduleSymbol, TypeDefinitionHandle handle, out GenericParameterHandleCollection genericParameterHandles, out ushort arity, out BadImageFormatException mrEx)
{
try
{
genericParameterHandles = moduleSymbol.Module.GetTypeDefGenericParamsOrThrow(handle);
arity = (ushort)genericParameterHandles.Count;
mrEx = null;
}
catch (BadImageFormatException e)
{
arity = 0;
genericParameterHandles = default(GenericParameterHandleCollection);
mrEx = e;
}
}
internal static PENamedTypeSymbol Create(
PEModuleSymbol moduleSymbol,
PENamedTypeSymbol containingType,
TypeDefinitionHandle handle)
{
GenericParameterHandleCollection genericParameterHandles;
ushort metadataArity;
BadImageFormatException mrEx = null;
GetGenericInfo(moduleSymbol, handle, out genericParameterHandles, out metadataArity, out mrEx);
ushort arity = 0;
var containerMetadataArity = containingType.MetadataArity;
if (metadataArity > containerMetadataArity)
{
arity = (ushort)(metadataArity - containerMetadataArity);
}
PENamedTypeSymbol result;
if (metadataArity == 0)
{
result = new PENamedTypeSymbolNonGeneric(moduleSymbol, containingType, handle, null);
}
else
{
result = new PENamedTypeSymbolGeneric(
moduleSymbol,
containingType,
handle,
null,
genericParameterHandles,
arity);
}
if (mrEx != null || metadataArity < containerMetadataArity)
{
result._lazyCachedUseSiteInfo.Initialize(result.DeriveCompilerFeatureRequiredDiagnostic() ?? new CSDiagnosticInfo(ErrorCode.ERR_BogusType, result));
}
return result;
}
private PENamedTypeSymbol(
PEModuleSymbol moduleSymbol,
NamespaceOrTypeSymbol container,
TypeDefinitionHandle handle,
string emittedNamespaceName,
ushort arity,
out bool mangleName)
{
Debug.Assert(!handle.IsNil);
Debug.Assert((object)container != null);
Debug.Assert(arity == 0 || this is PENamedTypeSymbolGeneric);
string metadataName;
bool makeBad = false;
try
{
metadataName = moduleSymbol.Module.GetTypeDefNameOrThrow(handle);
}
catch (BadImageFormatException)
{
metadataName = string.Empty;
makeBad = true;
}
_handle = handle;
_container = container;
try
{
_flags = moduleSymbol.Module.GetTypeDefFlagsOrThrow(handle);
}
catch (BadImageFormatException)
{
makeBad = true;
}
if (arity == 0)
{
_name = metadataName;
mangleName = false;
}
else
{
// Unmangle name for a generic type.
_name = MetadataHelpers.UnmangleMetadataNameForArity(metadataName, arity);
Debug.Assert(ReferenceEquals(_name, metadataName) == (_name == metadataName));
mangleName = !ReferenceEquals(_name, metadataName);
}
if (_lazyUncommonProperties is not null)
{
throw ExceptionUtilities.Unreachable();
}
// when a file-local type from source is loaded from metadata, we do a best-effort check to identify it as a file type
// this is needed to allow EE to bind to file types from metadata, for example.
if (container.IsNamespace && GeneratedNameParser.TryParseFileTypeName(_name, out var displayFileName, out var ordinal, out var originalTypeName))
{
_name = originalTypeName;
_lazyUncommonProperties = new UncommonProperties()
{
lazyFilePathChecksum = ordinal.ToImmutableArray(),
lazyDisplayFileName = displayFileName
};
}
// check if this is one of the COR library types
if (emittedNamespaceName != null &&
moduleSymbol.ContainingAssembly.KeepLookingForDeclaredSpecialTypes &&
this.DeclaredAccessibility == Accessibility.Public) // NB: this.flags was set above.
{
_corTypeId = SpecialTypes.GetTypeFromMetadataName(MetadataHelpers.BuildQualifiedName(emittedNamespaceName, metadataName));
}
else
{
_corTypeId = SpecialType.None;
}
if (makeBad)
{
_lazyCachedUseSiteInfo.Initialize(DeriveCompilerFeatureRequiredDiagnostic() ?? new CSDiagnosticInfo(ErrorCode.ERR_BogusType, this));
}
}
public override ExtendedSpecialType ExtendedSpecialType
{
get
{
return _corTypeId;
}
}
internal PEModuleSymbol ContainingPEModule
{
get
{
Symbol s = _container;
while (s.Kind != SymbolKind.Namespace)
{
s = s.ContainingSymbol;
}
return ((PENamespaceSymbol)s).ContainingPEModule;
}
}
internal override ModuleSymbol ContainingModule
{
get
{
return ContainingPEModule;
}
}
public abstract override int Arity
{
get;
}
internal abstract override bool MangleName
{
get;
}
internal sealed override bool IsFileLocal => _lazyUncommonProperties is { lazyFilePathChecksum: { IsDefault: false }, lazyDisplayFileName: { } };
internal sealed override FileIdentifier AssociatedFileIdentifier
{
get
{
// `lazyFilePathChecksum` and `lazyDisplayFileName` of `_lazyUncommonProperties` are initialized in the constructor, not on demand.
// Therefore we can use `_lazyUncommonProperties` directly to avoid additional computations.
// Also important, that computing full uncommon properties here may lead to stack overflow if there is a circular dependency between types in the metadata.
return _lazyUncommonProperties is { lazyFilePathChecksum: { IsDefault: false } checksum, lazyDisplayFileName: { } displayFileName }
? FileIdentifier.Create(checksum, displayFileName)
: null;
}
}
internal abstract int MetadataArity
{
get;
}
internal TypeDefinitionHandle Handle
{
get
{
return _handle;
}
}
public override int MetadataToken
{
get { return MetadataTokens.GetToken(_handle); }
}
internal sealed override bool IsInterpolatedStringHandlerType
{
get
{
var uncommon = GetUncommonProperties();
if (uncommon == s_noUncommonProperties)
{
return false;
}
if (!uncommon.lazyHasInterpolatedStringHandlerAttribute.HasValue())
{
uncommon.lazyHasInterpolatedStringHandlerAttribute = ContainingPEModule.Module.HasInterpolatedStringHandlerAttribute(_handle).ToThreeState();
}
return uncommon.lazyHasInterpolatedStringHandlerAttribute.Value();
}
}
internal override bool HasCodeAnalysisEmbeddedAttribute
{
get
{
var uncommon = GetUncommonProperties();
if (uncommon == s_noUncommonProperties)
{
return false;
}
if (!uncommon.lazyHasEmbeddedAttribute.HasValue())
{
uncommon.lazyHasEmbeddedAttribute = ContainingPEModule.Module.HasCodeAnalysisEmbeddedAttribute(_handle).ToThreeState();
}
return uncommon.lazyHasEmbeddedAttribute.Value();
}
}
internal override NamedTypeSymbol BaseTypeNoUseSiteDiagnostics
{
get
{
if (ReferenceEquals(_lazyBaseType, ErrorTypeSymbol.UnknownResultType))
{
Interlocked.CompareExchange(ref _lazyBaseType, MakeAcyclicBaseType(), ErrorTypeSymbol.UnknownResultType);
}
return _lazyBaseType;
}
}
internal override ImmutableArray<NamedTypeSymbol> InterfacesNoUseSiteDiagnostics(ConsList<TypeSymbol> basesBeingResolved = null)
{
if (_lazyInterfaces.IsDefault)
{
ImmutableInterlocked.InterlockedCompareExchange(ref _lazyInterfaces, MakeAcyclicInterfaces(), default(ImmutableArray<NamedTypeSymbol>));
}
return _lazyInterfaces;
}
internal override ImmutableArray<NamedTypeSymbol> GetInterfacesToEmit()
{
return InterfacesNoUseSiteDiagnostics();
}
internal override NamedTypeSymbol GetDeclaredBaseType(ConsList<TypeSymbol> basesBeingResolved)
{
return GetDeclaredBaseType(skipTransformsIfNecessary: false);
}
private NamedTypeSymbol GetDeclaredBaseType(bool skipTransformsIfNecessary)
{
if (ReferenceEquals(_lazyDeclaredBaseType, ErrorTypeSymbol.UnknownResultType))
{
var baseType = MakeDeclaredBaseType();
if (baseType is object)
{
if (skipTransformsIfNecessary)
{
// If the transforms are not necessary, return early without updating the
// base type field. This avoids cycles decoding nullability in particular.
return baseType;
}
var moduleSymbol = ContainingPEModule;
TypeSymbol decodedType = DynamicTypeDecoder.TransformType(baseType, 0, _handle, moduleSymbol);
decodedType = NativeIntegerTypeDecoder.TransformType(decodedType, _handle, moduleSymbol, this);
decodedType = TupleTypeDecoder.DecodeTupleTypesIfApplicable(decodedType, _handle, moduleSymbol);
baseType = (NamedTypeSymbol)NullableTypeDecoder.TransformType(TypeWithAnnotations.Create(decodedType), _handle, moduleSymbol, accessSymbol: this, nullableContext: this).Type;
}
Interlocked.CompareExchange(ref _lazyDeclaredBaseType, baseType, ErrorTypeSymbol.UnknownResultType);
}
return _lazyDeclaredBaseType;
}
internal override ImmutableArray<NamedTypeSymbol> GetDeclaredInterfaces(ConsList<TypeSymbol> basesBeingResolved)
{
if (_lazyDeclaredInterfaces.IsDefault)
{
ImmutableInterlocked.InterlockedCompareExchange(ref _lazyDeclaredInterfaces, MakeDeclaredInterfaces(), default(ImmutableArray<NamedTypeSymbol>));
}
return _lazyDeclaredInterfaces;
}
private NamedTypeSymbol MakeDeclaredBaseType()
{
if (!_flags.IsInterface())
{
try
{
var moduleSymbol = ContainingPEModule;
EntityHandle token = moduleSymbol.Module.GetBaseTypeOfTypeOrThrow(_handle);
if (!token.IsNil)
{
return (NamedTypeSymbol)new MetadataDecoder(moduleSymbol, this).GetTypeOfToken(token);
}
}
catch (BadImageFormatException mrEx)
{
return new UnsupportedMetadataTypeSymbol(mrEx);
}
}
return null;
}
private ImmutableArray<NamedTypeSymbol> MakeDeclaredInterfaces()
{
try
{
var moduleSymbol = ContainingPEModule;
var interfaceImpls = moduleSymbol.Module.GetInterfaceImplementationsOrThrow(_handle);
if (interfaceImpls.Count > 0)
{
var symbols = ArrayBuilder<NamedTypeSymbol>.GetInstance(interfaceImpls.Count);
var tokenDecoder = new MetadataDecoder(moduleSymbol, this);
foreach (var interfaceImpl in interfaceImpls)
{
EntityHandle interfaceHandle = moduleSymbol.Module.MetadataReader.GetInterfaceImplementation(interfaceImpl).Interface;
TypeSymbol typeSymbol = tokenDecoder.GetTypeOfToken(interfaceHandle);
typeSymbol = NativeIntegerTypeDecoder.TransformType(typeSymbol, interfaceImpl, moduleSymbol, ContainingType);
typeSymbol = TupleTypeDecoder.DecodeTupleTypesIfApplicable(typeSymbol, interfaceImpl, moduleSymbol);
typeSymbol = NullableTypeDecoder.TransformType(TypeWithAnnotations.Create(typeSymbol), interfaceImpl, moduleSymbol, accessSymbol: this, nullableContext: this).Type;
var namedTypeSymbol = typeSymbol as NamedTypeSymbol ?? new UnsupportedMetadataTypeSymbol(); // interface list contains a bad type
symbols.Add(namedTypeSymbol);
}
return symbols.ToImmutableAndFree();
}
return ImmutableArray<NamedTypeSymbol>.Empty;
}
catch (BadImageFormatException mrEx)
{
return ImmutableArray.Create<NamedTypeSymbol>(new UnsupportedMetadataTypeSymbol(mrEx));
}
}
public override NamedTypeSymbol ConstructedFrom
{
get
{
return this;
}
}
public override Symbol ContainingSymbol
{
get
{
return _container;
}
}
public override NamedTypeSymbol ContainingType
{
get
{
return _container as NamedTypeSymbol;
}
}
internal override bool IsRecord
{
get
{
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
return SynthesizedRecordClone.FindValidCloneMethod(this, ref discardedUseSiteInfo) != null;
}
}
// Record structs get erased when emitted to metadata
internal override bool IsRecordStruct => false;
public override Accessibility DeclaredAccessibility
{
get
{
Accessibility access = Accessibility.Private;
switch (_flags & TypeAttributes.VisibilityMask)
{
case TypeAttributes.NestedAssembly:
access = Accessibility.Internal;
break;
case TypeAttributes.NestedFamORAssem:
access = Accessibility.ProtectedOrInternal;
break;
case TypeAttributes.NestedFamANDAssem:
access = Accessibility.ProtectedAndInternal;
break;
case TypeAttributes.NestedPrivate:
access = Accessibility.Private;
break;
case TypeAttributes.Public:
case TypeAttributes.NestedPublic:
access = Accessibility.Public;
break;
case TypeAttributes.NestedFamily:
access = Accessibility.Protected;
break;
case TypeAttributes.NotPublic:
access = Accessibility.Internal;
break;
default:
throw ExceptionUtilities.UnexpectedValue(_flags & TypeAttributes.VisibilityMask);
}
return access;
}
}
public override NamedTypeSymbol EnumUnderlyingType
{
get
{
var uncommon = GetUncommonProperties();
if (uncommon == s_noUncommonProperties)
{
return null;
}
this.EnsureEnumUnderlyingTypeIsLoaded(uncommon);
return uncommon.lazyEnumUnderlyingType;
}
}
public override ImmutableArray<CSharpAttributeData> GetAttributes()
{
var uncommon = GetUncommonProperties();
if (uncommon == s_noUncommonProperties)
{
return ImmutableArray<CSharpAttributeData>.Empty;
}
if (uncommon.lazyCustomAttributes.IsDefault)
{
var loadedCustomAttributes = ContainingPEModule.GetCustomAttributesForToken(
Handle,
out _,
// Filter out [Extension]
MightContainExtensionMethods ? AttributeDescription.CaseSensitiveExtensionAttribute : default,
out _,
// Filter out [Obsolete], unless it was user defined
(IsRefLikeType && ObsoleteAttributeData is null) ? AttributeDescription.ObsoleteAttribute : default,
out _,
// Filter out [IsReadOnly]
IsReadOnly ? AttributeDescription.IsReadOnlyAttribute : default,
out _,
// Filter out [IsByRefLike]
IsRefLikeType ? AttributeDescription.IsByRefLikeAttribute : default,
out _,
// Filter out [CompilerFeatureRequired]
(IsRefLikeType && DeriveCompilerFeatureRequiredDiagnostic() is null) ? AttributeDescription.CompilerFeatureRequiredAttribute : default,
out CustomAttributeHandle requiredHandle,
// Filter out [RequiredMember]
AttributeDescription.RequiredMemberAttribute);
ImmutableInterlocked.InterlockedInitialize(ref uncommon.lazyCustomAttributes, loadedCustomAttributes);
if (!uncommon.lazyHasRequiredMembers.HasValue())
{
uncommon.lazyHasRequiredMembers = (!requiredHandle.IsNil).ToThreeState();
}
Debug.Assert(uncommon.lazyHasRequiredMembers.Value() != requiredHandle.IsNil);
}
return uncommon.lazyCustomAttributes;
}
internal override IEnumerable<CSharpAttributeData> GetCustomAttributesToEmit(PEModuleBuilder moduleBuilder)
{
return GetAttributes();
}
internal override byte? GetNullableContextValue()
{
byte? value;
if (!_lazyNullableContextValue.TryGetByte(out value))
{
value = ContainingPEModule.Module.HasNullableContextAttribute(_handle, out byte arg) ?
arg :
_container.GetNullableContextValue();
_lazyNullableContextValue = value.ToNullableContextFlags();
}
return value;
}
internal override byte? GetLocalNullableContextValue()
{
throw ExceptionUtilities.Unreachable();
}
public override IEnumerable<string> MemberNames
{
get
{
EnsureNonTypeMemberNamesAreLoaded();
return _lazyMemberNames;
}
}
private void EnsureNonTypeMemberNamesAreLoaded()
{
if (_lazyMemberNames == null)
{
var moduleSymbol = ContainingPEModule;
var module = moduleSymbol.Module;
var names = new HashSet<string>();
try
{
foreach (var methodDef in module.GetMethodsOfTypeOrThrow(_handle))
{
try
{
names.Add(module.GetMethodDefNameOrThrow(methodDef));
}
catch (BadImageFormatException)
{ }
}
}
catch (BadImageFormatException)
{ }
try
{
foreach (var propertyDef in module.GetPropertiesOfTypeOrThrow(_handle))
{
try
{
names.Add(module.GetPropertyDefNameOrThrow(propertyDef));
}
catch (BadImageFormatException)
{ }
}
}
catch (BadImageFormatException)
{ }
try
{
foreach (var eventDef in module.GetEventsOfTypeOrThrow(_handle))
{
try
{
names.Add(module.GetEventDefNameOrThrow(eventDef));
}
catch (BadImageFormatException)
{ }
}
}
catch (BadImageFormatException)
{ }
try
{
foreach (var fieldDef in module.GetFieldsOfTypeOrThrow(_handle))
{
try
{
names.Add(module.GetFieldDefNameOrThrow(fieldDef));
}
catch (BadImageFormatException)
{ }
}
}
catch (BadImageFormatException)
{ }
// From C#'s perspective, structs always have a public constructor
// (even if it's not in metadata). Add it unconditionally and let
// the hash set de-dup.
if (this.IsValueType)
{
names.Add(WellKnownMemberNames.InstanceConstructorName);
}
Interlocked.CompareExchange(ref _lazyMemberNames, CreateReadOnlyMemberNames(names), null);
}
}
private static ICollection<string> CreateReadOnlyMemberNames(HashSet<string> names)
{
switch (names.Count)
{
case 0:
return SpecializedCollections.EmptySet<string>();
case 1:
return SpecializedCollections.SingletonCollection(names.First());
case 2:
case 3:
case 4:
case 5:
case 6:
// PERF: Small collections can be implemented as ImmutableArray.
// While lookup is O(n), when n is small, the memory savings are more valuable.
// Size 6 was chosen because that represented 50% of the names generated in the Picasso end to end.
// This causes boxing, but that's still superior to a wrapped HashSet
return ImmutableArray.CreateRange(names);
default:
return SpecializedCollections.ReadOnlySet(names);
}
}
internal override bool HasDeclaredRequiredMembers
{
get
{
var uncommon = GetUncommonProperties();
if (uncommon == s_noUncommonProperties)
{
return false;
}
if (uncommon.lazyHasRequiredMembers.HasValue())
{
return uncommon.lazyHasRequiredMembers.Value();
}
var hasRequiredMemberAttribute = ContainingPEModule.Module.HasAttribute(_handle, AttributeDescription.RequiredMemberAttribute);
uncommon.lazyHasRequiredMembers = hasRequiredMemberAttribute.ToThreeState();
return hasRequiredMemberAttribute;
}
}
public override ImmutableArray<Symbol> GetMembers()
{
EnsureAllMembersAreLoaded();
return _lazyMembersInDeclarationOrder;
}
private IEnumerable<FieldSymbol> GetEnumFieldsToEmit()
{
var uncommon = GetUncommonProperties();
if (uncommon == s_noUncommonProperties)
{
yield break;
}
var moduleSymbol = this.ContainingPEModule;
var module = moduleSymbol.Module;
// Non-static fields of enum types are not imported by default because they are not bindable,
// but we need them for NoPia.
var fieldDefs = ArrayBuilder<FieldDefinitionHandle>.GetInstance();
try
{
foreach (var fieldDef in module.GetFieldsOfTypeOrThrow(_handle))
{
fieldDefs.Add(fieldDef);
}
}
catch (BadImageFormatException)
{ }
if (uncommon.lazyInstanceEnumFields.IsDefault)
{
var builder = ArrayBuilder<PEFieldSymbol>.GetInstance();
foreach (var fieldDef in fieldDefs)
{
try
{
FieldAttributes fieldFlags = module.GetFieldDefFlagsOrThrow(fieldDef);
if ((fieldFlags & FieldAttributes.Static) == 0 && ModuleExtensions.ShouldImportField(fieldFlags, moduleSymbol.ImportOptions))
{
builder.Add(new PEFieldSymbol(moduleSymbol, this, fieldDef));
}
}
catch (BadImageFormatException)
{ }
}
ImmutableInterlocked.InterlockedInitialize(ref uncommon.lazyInstanceEnumFields, builder.ToImmutableAndFree());
}
int staticIndex = 0;
ImmutableArray<Symbol> staticFields = GetMembers();
int instanceIndex = 0;
foreach (var fieldDef in fieldDefs)
{
if (instanceIndex < uncommon.lazyInstanceEnumFields.Length && uncommon.lazyInstanceEnumFields[instanceIndex].Handle == fieldDef)
{
yield return uncommon.lazyInstanceEnumFields[instanceIndex];
instanceIndex++;
continue;
}
if (staticIndex < staticFields.Length && staticFields[staticIndex].Kind == SymbolKind.Field)
{
var field = (PEFieldSymbol)staticFields[staticIndex];
if (field.Handle == fieldDef)
{
yield return field;
staticIndex++;
continue;
}
}
}
fieldDefs.Free();
Debug.Assert(instanceIndex == uncommon.lazyInstanceEnumFields.Length);
Debug.Assert(staticIndex == staticFields.Length || staticFields[staticIndex].Kind != SymbolKind.Field);
}
internal override IEnumerable<FieldSymbol> GetFieldsToEmit()
{
if (this.TypeKind == TypeKind.Enum)
{
return GetEnumFieldsToEmit();
}
else
{
// If there are any non-event fields, they are at the very beginning.
IEnumerable<FieldSymbol> nonEventFields = GetMembers<FieldSymbol>(this.GetMembers().WhereAsArray(m => !(m is TupleErrorFieldSymbol)), SymbolKind.Field, offset: 0);
// Event backing fields are not part of the set returned by GetMembers. Let's add them manually.
ArrayBuilder<FieldSymbol> eventFields = null;
foreach (var eventSymbol in GetEventsToEmit())
{
FieldSymbol associatedField = eventSymbol.AssociatedField;
if ((object)associatedField != null)
{
Debug.Assert((object)associatedField.AssociatedSymbol != null);
Debug.Assert(!nonEventFields.Contains(associatedField));
if (eventFields == null)
{
eventFields = ArrayBuilder<FieldSymbol>.GetInstance();
}
eventFields.Add(associatedField);
}
}
if (eventFields == null)
{
// Simple case
return nonEventFields;
}
// We need to merge non-event fields with event fields while preserving their relative declaration order
var handleToFieldMap = new SmallDictionary<FieldDefinitionHandle, FieldSymbol>();
int count = 0;
foreach (PEFieldSymbol field in nonEventFields)
{
handleToFieldMap.Add(field.Handle, field);
count++;
}
foreach (PEFieldSymbol field in eventFields)
{
handleToFieldMap.Add(field.Handle, field);
}
count += eventFields.Count;
eventFields.Free();
var result = ArrayBuilder<FieldSymbol>.GetInstance(count);
try
{
foreach (var handle in this.ContainingPEModule.Module.GetFieldsOfTypeOrThrow(_handle))
{
FieldSymbol field;
if (handleToFieldMap.TryGetValue(handle, out field))
{
result.Add(field);
}
}
}
catch (BadImageFormatException)
{ }
Debug.Assert(result.Count == count);
return result.ToImmutableAndFree();
}
}
internal override IEnumerable<MethodSymbol> GetMethodsToEmit()
{
ImmutableArray<Symbol> members = GetMembers();
// Get to methods.
int index = GetIndexOfFirstMember(members, SymbolKind.Method);
if (!this.IsInterfaceType())
{
for (; index < members.Length; index++)
{
if (members[index].Kind != SymbolKind.Method)
{
break;
}
var method = (MethodSymbol)members[index];
// Don't emit the default value type constructor - the runtime handles that.
if (!method.IsDefaultValueTypeConstructor())
{
yield return method;
}
}
}
else
{
// We do not create symbols for v-table gap methods, let's figure out where the gaps go.
if (index >= members.Length || members[index].Kind != SymbolKind.Method)
{
// We didn't import any methods, it is Ok to return an empty set.
yield break;
}
var method = (PEMethodSymbol)members[index];
var module = this.ContainingPEModule.Module;
var methodDefs = ArrayBuilder<MethodDefinitionHandle>.GetInstance();
try
{
foreach (var methodDef in module.GetMethodsOfTypeOrThrow(_handle))
{
methodDefs.Add(methodDef);
}
}
catch (BadImageFormatException)
{ }
foreach (var methodDef in methodDefs)
{
if (method.Handle == methodDef)
{
yield return method;
index++;
if (index == members.Length || members[index].Kind != SymbolKind.Method)
{
// no need to return any gaps at the end.
methodDefs.Free();
yield break;
}
method = (PEMethodSymbol)members[index];
}
else
{
// Encountered a gap.
int gapSize;
try
{
gapSize = ModuleExtensions.GetVTableGapSize(module.GetMethodDefNameOrThrow(methodDef));
}
catch (BadImageFormatException)
{
gapSize = 1;
}
// We don't have a symbol to return, so, even if the name doesn't represent a gap, we still have a gap.
do
{
yield return null;
gapSize--;
}
while (gapSize > 0);
}
}
// Ensure we explicitly returned from inside loop.
throw ExceptionUtilities.Unreachable();
}
}
internal override IEnumerable<PropertySymbol> GetPropertiesToEmit()
{
return GetMembers<PropertySymbol>(this.GetMembers(), SymbolKind.Property);
}
internal override IEnumerable<EventSymbol> GetEventsToEmit()
{
return GetMembers<EventSymbol>(this.GetMembers(), SymbolKind.Event);
}
internal override ImmutableArray<Symbol> GetEarlyAttributeDecodingMembers()
{
return this.GetMembersUnordered();
}
internal override ImmutableArray<Symbol> GetEarlyAttributeDecodingMembers(string name)
{
return this.GetMembers(name);
}
private class DeclarationOrderTypeSymbolComparer : IComparer<Symbol>
{
public static readonly DeclarationOrderTypeSymbolComparer Instance = new DeclarationOrderTypeSymbolComparer();
private DeclarationOrderTypeSymbolComparer() { }
public int Compare(Symbol x, Symbol y)
{
return HandleComparer.Default.Compare(((PENamedTypeSymbol)x).Handle, ((PENamedTypeSymbol)y).Handle);
}
}
private void EnsureEnumUnderlyingTypeIsLoaded(UncommonProperties uncommon)
{
if ((object)(uncommon.lazyEnumUnderlyingType) == null
&& this.TypeKind == TypeKind.Enum)
{
// From §8.5.2
// An enum is considerably more restricted than a true type, as
// follows:
// - It shall have exactly one instance field, and the type of that field defines the underlying type of
// the enumeration.
// - It shall not have any static fields unless they are literal. (see §8.6.1.2)
// The underlying type shall be a built-in integer type. Enums shall derive from System.Enum, hence they are
// value types. Like all value types, they shall be sealed (see §8.9.9).
var moduleSymbol = this.ContainingPEModule;
var module = moduleSymbol.Module;
var decoder = new MetadataDecoder(moduleSymbol, this);
NamedTypeSymbol underlyingType = null;
try
{
foreach (var fieldDef in module.GetFieldsOfTypeOrThrow(_handle))
{
FieldAttributes fieldFlags;
try
{
fieldFlags = module.GetFieldDefFlagsOrThrow(fieldDef);
}
catch (BadImageFormatException)
{
continue;
}
if ((fieldFlags & FieldAttributes.Static) == 0)
{
// Instance field used to determine underlying type.
FieldInfo<TypeSymbol> fieldInfo = decoder.DecodeFieldSignature(fieldDef);
TypeSymbol type = fieldInfo.Type;
if (type.SpecialType.IsValidEnumUnderlyingType() &&
!fieldInfo.IsByRef &&
!fieldInfo.CustomModifiers.AnyRequired())
{
if ((object)underlyingType == null)
{
underlyingType = (NamedTypeSymbol)type;
}
else
{
underlyingType = new UnsupportedMetadataTypeSymbol(); // ambiguous underlying type
}
}
}
}
if ((object)underlyingType == null)
{
underlyingType = new UnsupportedMetadataTypeSymbol(); // undefined underlying type
}
}
catch (BadImageFormatException mrEx)
{
if ((object)underlyingType == null)
{
underlyingType = new UnsupportedMetadataTypeSymbol(mrEx);
}
}
Interlocked.CompareExchange(ref uncommon.lazyEnumUnderlyingType, underlyingType, null);
}
}
private void EnsureAllMembersAreLoaded()
{
if (_lazyMembersByName == null)
{
LoadMembers();
}
}
private void LoadMembers()
{
ArrayBuilder<Symbol> members = null;
if (_lazyMembersInDeclarationOrder.IsDefault)
{
EnsureNestedTypesAreLoaded();
members = ArrayBuilder<Symbol>.GetInstance();
Debug.Assert(SymbolKind.Field.ToSortOrder() < SymbolKind.Method.ToSortOrder());
Debug.Assert(SymbolKind.Method.ToSortOrder() < SymbolKind.Property.ToSortOrder());
Debug.Assert(SymbolKind.Property.ToSortOrder() < SymbolKind.Event.ToSortOrder());
Debug.Assert(SymbolKind.Event.ToSortOrder() < SymbolKind.NamedType.ToSortOrder());
if (this.TypeKind == TypeKind.Enum)
{
EnsureEnumUnderlyingTypeIsLoaded(this.GetUncommonProperties());
var moduleSymbol = this.ContainingPEModule;
var module = moduleSymbol.Module;
try
{
foreach (var fieldDef in module.GetFieldsOfTypeOrThrow(_handle))
{
FieldAttributes fieldFlags;
try
{
fieldFlags = module.GetFieldDefFlagsOrThrow(fieldDef);
if ((fieldFlags & FieldAttributes.Static) == 0)
{
continue;
}
}
catch (BadImageFormatException)
{
fieldFlags = 0;
}
if (ModuleExtensions.ShouldImportField(fieldFlags, moduleSymbol.ImportOptions))
{
var field = new PEFieldSymbol(moduleSymbol, this, fieldDef);
members.Add(field);
}
}
}
catch (BadImageFormatException)
{ }
var syntheticCtor = new SynthesizedInstanceConstructor(this);
members.Add(syntheticCtor);
}
else
{
ArrayBuilder<PEFieldSymbol> fieldMembers = ArrayBuilder<PEFieldSymbol>.GetInstance();
ArrayBuilder<Symbol> nonFieldMembers = ArrayBuilder<Symbol>.GetInstance();
MultiDictionary<string, PEFieldSymbol> privateFieldNameToSymbols = this.CreateFields(fieldMembers);
// A method may be referenced as an accessor by one or more properties. And,
// any of those properties may be "bogus" if one of the property accessors
// does not match the property signature. If the method is referenced by at
// least one non-bogus property, then the method is created as an accessor,
// and (for purposes of error reporting if the method is referenced directly) the
// associated property is set (arbitrarily) to the first non-bogus property found
// in metadata. If the method is not referenced by any non-bogus properties,
// then the method is created as a normal method rather than an accessor.
// Create a dictionary of method symbols indexed by metadata handle
// (to allow efficient lookup when matching property accessors).
PooledDictionary<MethodDefinitionHandle, PEMethodSymbol> methodHandleToSymbol = this.CreateMethods(nonFieldMembers);
if (this.TypeKind == TypeKind.Struct)
{
bool haveParameterlessConstructor = false;
foreach (MethodSymbol method in nonFieldMembers)
{
if (method.IsParameterlessConstructor())
{
haveParameterlessConstructor = true;
break;
}
}
// Structs have an implicit parameterless constructor, even if it
// does not appear in metadata (11.3.8)
if (!haveParameterlessConstructor)
{
nonFieldMembers.Insert(0, new SynthesizedInstanceConstructor(this));
}
}
this.CreateProperties(methodHandleToSymbol, nonFieldMembers);
this.CreateEvents(privateFieldNameToSymbols, methodHandleToSymbol, nonFieldMembers);
foreach (PEFieldSymbol field in fieldMembers)
{
if ((object)field.AssociatedSymbol == null)
{
members.Add(field);
}
else
{
// As for source symbols, our public API presents the fiction that all
// operations are performed on the event, rather than on the backing field.
// The backing field is not accessible through the API. As an additional
// bonus, lookup is easier when the names don't collide.
Debug.Assert(field.AssociatedSymbol.Kind == SymbolKind.Event);
}
}
members.AddRange(nonFieldMembers);
nonFieldMembers.Free();
fieldMembers.Free();
methodHandleToSymbol.Free();
}
// Now add types to the end.
int membersCount = members.Count;
foreach (var typeArray in _lazyNestedTypes.Values)
{
members.AddRange(typeArray);
}
// Sort the types based on row id.
members.Sort(membersCount, DeclarationOrderTypeSymbolComparer.Instance);
#if DEBUG
Symbol previous = null;
foreach (var s in members)
{
if (previous == null)
{
previous = s;
}
else
{
Symbol current = s;
Debug.Assert(previous.Kind.ToSortOrder() <= current.Kind.ToSortOrder());
previous = current;
}
}
#endif
if (IsTupleType)
{
int originalCount = members.Count;
var peMembers = members.ToImmutableAndFree();
members = MakeSynthesizedTupleMembers(peMembers);
membersCount += members.Count; // account for added tuple error fields
members.AddRange(peMembers);
Debug.Assert(members is object);
}
var membersInDeclarationOrder = members.ToImmutable();
if (!ImmutableInterlocked.InterlockedInitialize(ref _lazyMembersInDeclarationOrder, membersInDeclarationOrder))
{
members.Free();
members = null;
}
else
{
// remove the types
members.Clip(membersCount);
}
}
if (_lazyMembersByName == null)
{
if (members == null)
{
members = ArrayBuilder<Symbol>.GetInstance();
foreach (var member in _lazyMembersInDeclarationOrder)
{
if (member.Kind == SymbolKind.NamedType)
{
break;
}
members.Add(member);
}
}
Dictionary<string, ImmutableArray<Symbol>> membersDict = GroupByName(members);
var exchangeResult = Interlocked.CompareExchange(ref _lazyMembersByName, membersDict, null);
if (exchangeResult == null)
{
// we successfully swapped in the members dictionary.
// Now, use these as the canonical member names. This saves us memory by not having
// two collections around at the same time with redundant data in them.
//
// NOTE(cyrusn): We must use an interlocked exchange here so that the full
// construction of this object will be seen from 'MemberNames'. Also, doing a
// straight InterlockedExchange here is the right thing to do. Consider the case
// where one thread is calling in through "MemberNames" while we are in the middle
// of this method. Either that thread will compute the member names and store it
// first (in which case we overwrite it), or we will store first (in which case
// their CompareExchange(..., ..., null) will fail. Either way, this will be certain
// to become the canonical set of member names.
//
// NOTE(cyrusn): This means that it is possible (and by design) for people to get a
// different object back when they call MemberNames multiple times. However, outside
// of object identity, both collections should appear identical to the user.
var memberNames = SpecializedCollections.ReadOnlyCollection(membersDict.Keys);
Interlocked.Exchange(ref _lazyMemberNames, memberNames);
}
}
if (members != null)
{
members.Free();
}
}
internal override ImmutableArray<Symbol> GetSimpleNonTypeMembers(string name)
{
EnsureAllMembersAreLoaded();
ImmutableArray<Symbol> m;
if (!_lazyMembersByName.TryGetValue(name, out m))
{
m = ImmutableArray<Symbol>.Empty;
}
return m;
}
public override ImmutableArray<Symbol> GetMembers(string name)
{
EnsureAllMembersAreLoaded();
ImmutableArray<Symbol> m;
if (!_lazyMembersByName.TryGetValue(name, out m))
{
m = ImmutableArray<Symbol>.Empty;
}
// nested types are not common, but we need to check just in case
ImmutableArray<PENamedTypeSymbol> t;
if (_lazyNestedTypes.TryGetValue(name.AsMemory(), out t))
{
m = m.Concat(StaticCast<Symbol>.From(t));
}
return m;
}
internal sealed override bool HasPossibleWellKnownCloneMethod()
=> MemberNames.Contains(WellKnownMemberNames.CloneMethodName);
internal override FieldSymbol FixedElementField
{
get
{
FieldSymbol result = null;
var candidates = this.GetMembers(FixedFieldImplementationType.FixedElementFieldName);
if (!candidates.IsDefault && candidates.Length == 1)
{
result = candidates[0] as FieldSymbol;
}
return result;
}
}
internal override ImmutableArray<NamedTypeSymbol> GetTypeMembersUnordered()
{
return GetTypeMembers().ConditionallyDeOrder();
}
public override ImmutableArray<NamedTypeSymbol> GetTypeMembers()
{
EnsureNestedTypesAreLoaded();
return GetMemberTypesPrivate();
}
private ImmutableArray<NamedTypeSymbol> GetMemberTypesPrivate()
{
var builder = ArrayBuilder<NamedTypeSymbol>.GetInstance();
foreach (var typeArray in _lazyNestedTypes.Values)
{
builder.AddRange(typeArray);
}
return builder.ToImmutableAndFree();
}
private void EnsureNestedTypesAreLoaded()
{
if (_lazyNestedTypes == null)
{
var types = ArrayBuilder<PENamedTypeSymbol>.GetInstance();
types.AddRange(this.CreateNestedTypes());
var typesDict = GroupByName(types);
var exchangeResult = Interlocked.CompareExchange(ref _lazyNestedTypes, typesDict, null);
if (exchangeResult == null)
{
// Build cache of TypeDef Tokens
// Potentially this can be done in the background.
var moduleSymbol = this.ContainingPEModule;
moduleSymbol.OnNewTypeDeclarationsLoaded(typesDict);
}
types.Free();
}
}
public override ImmutableArray<NamedTypeSymbol> GetTypeMembers(ReadOnlyMemory<char> name)
{
EnsureNestedTypesAreLoaded();
ImmutableArray<PENamedTypeSymbol> t;
if (_lazyNestedTypes.TryGetValue(name, out t))
{
return StaticCast<NamedTypeSymbol>.From(t);
}
return ImmutableArray<NamedTypeSymbol>.Empty;
}
public override ImmutableArray<NamedTypeSymbol> GetTypeMembers(ReadOnlyMemory<char> name, int arity)
{
return GetTypeMembers(name).WhereAsArray((type, arity) => type.Arity == arity, arity);
}
public override ImmutableArray<Location> Locations
{
get
{
return ContainingPEModule.MetadataLocation.Cast<MetadataLocation, Location>();
}
}
public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences
{
get
{
return ImmutableArray<SyntaxReference>.Empty;
}
}
public override string Name
{
get
{
return _name;
}
}
internal override bool HasSpecialName
{
get
{
return (_flags & TypeAttributes.SpecialName) != 0;
}
}
internal override ImmutableArray<TypeWithAnnotations> TypeArgumentsWithAnnotationsNoUseSiteDiagnostics
{
get
{
return ImmutableArray<TypeWithAnnotations>.Empty;
}
}
public override ImmutableArray<TypeParameterSymbol> TypeParameters
{
get
{
return ImmutableArray<TypeParameterSymbol>.Empty;
}
}
public override bool IsStatic
{
get
{
return
(_flags & TypeAttributes.Sealed) != 0 &&
(_flags & TypeAttributes.Abstract) != 0;
}
}
public override bool IsAbstract
{
get
{
return
(_flags & TypeAttributes.Abstract) != 0 &&
(_flags & TypeAttributes.Sealed) == 0;
}
}
internal override bool IsMetadataAbstract
{
get
{
return (_flags & TypeAttributes.Abstract) != 0;
}
}
public override bool IsSealed
{
get
{
return
(_flags & TypeAttributes.Sealed) != 0 &&
(_flags & TypeAttributes.Abstract) == 0;
}
}
internal override bool IsMetadataSealed
{
get
{
return (_flags & TypeAttributes.Sealed) != 0;
}
}
internal TypeAttributes Flags
{
get
{
return _flags;
}
}
public sealed override bool AreLocalsZeroed
{
get { throw ExceptionUtilities.Unreachable(); }
}
public override bool MightContainExtensionMethods
{
get
{
var uncommon = GetUncommonProperties();
if (uncommon == s_noUncommonProperties)
{
return false;
}
if (!uncommon.lazyContainsExtensionMethods.HasValue())
{
var contains = ThreeState.False;
// Dev11 supports extension methods defined on non-static
// classes, structs, delegates, and generic types.
switch (this.TypeKind)
{
case TypeKind.Class:
case TypeKind.Struct:
case TypeKind.Delegate:
var moduleSymbol = this.ContainingPEModule;
var module = moduleSymbol.Module;
bool moduleHasExtension = module.HasExtensionAttribute(_handle, ignoreCase: false);
var containingAssembly = this.ContainingAssembly as PEAssemblySymbol;
if ((object)containingAssembly != null)
{
contains = (moduleHasExtension
&& containingAssembly.MightContainExtensionMethods).ToThreeState();
}
else
{
contains = moduleHasExtension.ToThreeState();
}
break;
}
uncommon.lazyContainsExtensionMethods = contains;
}
return uncommon.lazyContainsExtensionMethods.Value();
}
}
public override TypeKind TypeKind
{
get
{
TypeKind result = _lazyKind;
if (result == TypeKind.Unknown)
{
if (_flags.IsInterface())
{
result = TypeKind.Interface;
}
else
{
TypeSymbol @base = GetDeclaredBaseType(skipTransformsIfNecessary: true);
result = TypeKind.Class;
if ((object)@base != null)
{
SpecialType baseCorTypeId = @base.SpecialType;
switch (baseCorTypeId)
{
case SpecialType.System_Enum:
// Enum
result = TypeKind.Enum;
break;
case SpecialType.System_MulticastDelegate:
// Delegate
result = TypeKind.Delegate;
break;
case SpecialType.System_ValueType:
// System.Enum is the only class that derives from ValueType
if (this.SpecialType != SpecialType.System_Enum)
{
// Struct
result = TypeKind.Struct;
}
break;
}
}
}
_lazyKind = result;
}
return result;
}
}
internal sealed override bool IsInterface
{
get
{
return _flags.IsInterface();
}
}
private static ExtendedErrorTypeSymbol CyclicInheritanceError(TypeSymbol declaredBase)
{
var info = new CSDiagnosticInfo(ErrorCode.ERR_ImportedCircularBase, declaredBase);
return new ExtendedErrorTypeSymbol(declaredBase, LookupResultKind.NotReferencable, info, true);
}
private NamedTypeSymbol MakeAcyclicBaseType()
{
NamedTypeSymbol declaredBase = GetDeclaredBaseType(null);
// implicit base is not interesting for metadata cycle detection
if ((object)declaredBase == null)
{
return null;
}
if (BaseTypeAnalysis.TypeDependsOn(declaredBase, this))
{
return CyclicInheritanceError(declaredBase);
}
this.SetKnownToHaveNoDeclaredBaseCycles();
return declaredBase;
}
private ImmutableArray<NamedTypeSymbol> MakeAcyclicInterfaces()
{
var declaredInterfaces = GetDeclaredInterfaces(null);
if (!IsInterface)
{
// only interfaces needs to check for inheritance cycles via interfaces.
return declaredInterfaces;
}
return declaredInterfaces
.SelectAsArray(t => BaseTypeAnalysis.TypeDependsOn(t, this) ? CyclicInheritanceError(t) : t);
}
public override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken))
{
return PEDocumentationCommentUtils.GetDocumentationComment(this, ContainingPEModule, preferredCulture, cancellationToken, ref _lazyDocComment);
}
private IEnumerable<PENamedTypeSymbol> CreateNestedTypes()
{
var moduleSymbol = this.ContainingPEModule;
var module = moduleSymbol.Module;
ImmutableArray<TypeDefinitionHandle> nestedTypeDefs;
try
{
nestedTypeDefs = module.GetNestedTypeDefsOrThrow(_handle);
}
catch (BadImageFormatException)
{
yield break;
}
// Currently, it appears that we must import ALL types, even private ones,
// in order to maintain language semantics. This is because a class may implement
// private interfaces, and we use the interfaces (even if inaccessible) to determine
// conversions. For example:
//
// public class A: IEnumerable<A.X>
// {
// private class X: ICloneable {}
// }
//
// Code compiling against A can convert A to IEnumerable<ICloneable>. Knowing this requires
// importing the type A.X.
foreach (var typeRid in nestedTypeDefs)
{
yield return PENamedTypeSymbol.Create(moduleSymbol, this, typeRid);
}
}
private MultiDictionary<string, PEFieldSymbol> CreateFields(ArrayBuilder<PEFieldSymbol> fieldMembers)
{
var privateFieldNameToSymbols = new MultiDictionary<string, PEFieldSymbol>();
var moduleSymbol = this.ContainingPEModule;
var module = moduleSymbol.Module;
// for ordinary struct types we import private fields so that we can distinguish empty structs from non-empty structs
var isOrdinaryStruct = false;
// for ordinary embeddable struct types we import private members so that we can report appropriate errors if the structure is used
var isOrdinaryEmbeddableStruct = false;
if (this.TypeKind == TypeKind.Struct)
{
if (this.SpecialType == Microsoft.CodeAnalysis.SpecialType.None)
{
isOrdinaryEmbeddableStruct = this.ContainingAssembly.IsLinked;
}
switch (this.SpecialType)
{
case SpecialType.System_Void:
case SpecialType.System_Boolean:
case SpecialType.System_Char:
case SpecialType.System_Byte:
case SpecialType.System_SByte:
case SpecialType.System_Int16:
case SpecialType.System_UInt16:
case SpecialType.System_Int32:
case SpecialType.System_UInt32:
case SpecialType.System_Int64:
case SpecialType.System_UInt64:
case SpecialType.System_Single:
case SpecialType.System_Double:
case SpecialType.System_Decimal:
case SpecialType.System_IntPtr:
case SpecialType.System_UIntPtr:
case SpecialType.System_DateTime:
case SpecialType.System_TypedReference:
case SpecialType.System_ArgIterator:
case SpecialType.System_RuntimeArgumentHandle:
case SpecialType.System_RuntimeFieldHandle:
case SpecialType.System_RuntimeMethodHandle:
case SpecialType.System_RuntimeTypeHandle:
isOrdinaryStruct = false;
break;
default:
isOrdinaryStruct = true;
break;
}
}
try
{
foreach (var fieldRid in module.GetFieldsOfTypeOrThrow(_handle))
{
try
{
if (!(isOrdinaryEmbeddableStruct ||
(isOrdinaryStruct && (module.GetFieldDefFlagsOrThrow(fieldRid) & FieldAttributes.Static) == 0) ||
module.ShouldImportField(fieldRid, moduleSymbol.ImportOptions)))
{
continue;
}
}
catch (BadImageFormatException)
{ }
var symbol = new PEFieldSymbol(moduleSymbol, this, fieldRid);
fieldMembers.Add(symbol);
// Only private fields are potentially backing fields for field-like events.
if (symbol.DeclaredAccessibility == Accessibility.Private)
{
var name = symbol.Name;
if (name.Length > 0)
{
privateFieldNameToSymbols.Add(name, symbol);
}
}
}
}
catch (BadImageFormatException)
{ }
return privateFieldNameToSymbols;
}
private PooledDictionary<MethodDefinitionHandle, PEMethodSymbol> CreateMethods(ArrayBuilder<Symbol> members)
{
var moduleSymbol = this.ContainingPEModule;
var module = moduleSymbol.Module;
var map = PooledDictionary<MethodDefinitionHandle, PEMethodSymbol>.GetInstance();
// for ordinary embeddable struct types we import private members so that we can report appropriate errors if the structure is used
var isOrdinaryEmbeddableStruct = (this.TypeKind == TypeKind.Struct) && (this.SpecialType == Microsoft.CodeAnalysis.SpecialType.None) && this.ContainingAssembly.IsLinked;
try
{
foreach (var methodHandle in module.GetMethodsOfTypeOrThrow(_handle))
{
if (isOrdinaryEmbeddableStruct || module.ShouldImportMethod(_handle, methodHandle, moduleSymbol.ImportOptions))
{
var method = new PEMethodSymbol(moduleSymbol, this, methodHandle);
members.Add(method);
map.Add(methodHandle, method);
}
}
}
catch (BadImageFormatException)
{ }
return map;
}
private void CreateProperties(Dictionary<MethodDefinitionHandle, PEMethodSymbol> methodHandleToSymbol, ArrayBuilder<Symbol> members)
{
var moduleSymbol = this.ContainingPEModule;
var module = moduleSymbol.Module;
try
{
foreach (var propertyDef in module.GetPropertiesOfTypeOrThrow(_handle))
{
try
{
var methods = module.GetPropertyMethodsOrThrow(propertyDef);
PEMethodSymbol getMethod = GetAccessorMethod(module, methodHandleToSymbol, _handle, methods.Getter);
PEMethodSymbol setMethod = GetAccessorMethod(module, methodHandleToSymbol, _handle, methods.Setter);
if (((object)getMethod != null) || ((object)setMethod != null))
{
members.Add(PEPropertySymbol.Create(moduleSymbol, this, propertyDef, getMethod, setMethod));
}
}
catch (BadImageFormatException)
{ }
}
}
catch (BadImageFormatException)
{ }
}
private void CreateEvents(
MultiDictionary<string, PEFieldSymbol> privateFieldNameToSymbols,
Dictionary<MethodDefinitionHandle, PEMethodSymbol> methodHandleToSymbol,
ArrayBuilder<Symbol> members)
{
var moduleSymbol = this.ContainingPEModule;
var module = moduleSymbol.Module;
try
{
foreach (var eventRid in module.GetEventsOfTypeOrThrow(_handle))
{
try
{
var methods = module.GetEventMethodsOrThrow(eventRid);
// NOTE: C# ignores all other accessors (most notably, raise/fire).
PEMethodSymbol addMethod = GetAccessorMethod(module, methodHandleToSymbol, _handle, methods.Adder);
PEMethodSymbol removeMethod = GetAccessorMethod(module, methodHandleToSymbol, _handle, methods.Remover);
// NOTE: both accessors are required, but that will be reported separately.
// Create the symbol unless both accessors are missing.
if (((object)addMethod != null) || ((object)removeMethod != null))
{
members.Add(new PEEventSymbol(moduleSymbol, this, eventRid, addMethod, removeMethod, privateFieldNameToSymbols));
}
}
catch (BadImageFormatException)
{ }
}
}
catch (BadImageFormatException)
{ }
}
private PEMethodSymbol GetAccessorMethod(PEModule module, Dictionary<MethodDefinitionHandle, PEMethodSymbol> methodHandleToSymbol, TypeDefinitionHandle typeDef, MethodDefinitionHandle methodDef)
{
if (methodDef.IsNil)
{
return null;
}
PEMethodSymbol method;
bool found = methodHandleToSymbol.TryGetValue(methodDef, out method);
Debug.Assert(found || !module.ShouldImportMethod(typeDef, methodDef, this.ContainingPEModule.ImportOptions));
return method;
}
private static Dictionary<string, ImmutableArray<Symbol>> GroupByName(ArrayBuilder<Symbol> symbols)
{
return symbols.ToDictionary(s => s.Name, StringOrdinalComparer.Instance);
}
private static Dictionary<ReadOnlyMemory<char>, ImmutableArray<PENamedTypeSymbol>> GroupByName(ArrayBuilder<PENamedTypeSymbol> symbols)
{
if (symbols.Count == 0)
{
return s_emptyNestedTypes;
}
return symbols.ToDictionary(s => s.Name.AsMemory(), ReadOnlyMemoryOfCharComparer.Instance);
}
internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
{
AssemblySymbol primaryDependency = PrimaryDependency;
if (!_lazyCachedUseSiteInfo.IsInitialized)
{
_lazyCachedUseSiteInfo.Initialize(primaryDependency, new UseSiteInfo<AssemblySymbol>(primaryDependency).AdjustDiagnosticInfo(GetUseSiteDiagnosticImpl()));
}
return _lazyCachedUseSiteInfo.ToUseSiteInfo(primaryDependency);
}
protected virtual DiagnosticInfo GetUseSiteDiagnosticImpl()
{
// GetCompilerFeatureRequiredDiagnostic depends on UnsupportedCompilerFeature being the highest priority diagnostic, or it will return incorrect
// results and assert in Debug mode.
DiagnosticInfo diagnostic = DeriveCompilerFeatureRequiredDiagnostic();
if (diagnostic != null)
{
return diagnostic;
}
if (!MergeUseSiteDiagnostics(ref diagnostic, CalculateUseSiteDiagnostic()))
{
// Check if this type is marked by RequiredAttribute attribute.
// If so mark the type as bad, because it relies upon semantics that are not understood by the C# compiler.
if (this.ContainingPEModule.Module.HasRequiredAttributeAttribute(_handle))
{
diagnostic = new CSDiagnosticInfo(ErrorCode.ERR_BogusType, this);
}
else if (TypeKind == TypeKind.Class && SpecialType != SpecialType.System_Enum)
{
TypeSymbol @base = GetDeclaredBaseType(null);
if (@base?.SpecialType == SpecialType.None && @base.ContainingAssembly?.IsMissing == true)
{
var missingType = @base as MissingMetadataTypeSymbol.TopLevel;
if ((object)missingType != null && missingType.Arity == 0)
{
string emittedName = MetadataHelpers.BuildQualifiedName(missingType.NamespaceName, missingType.MetadataName);
switch ((SpecialType)SpecialTypes.GetTypeFromMetadataName(emittedName))
{
case SpecialType.System_Enum:
case SpecialType.System_MulticastDelegate:
case SpecialType.System_ValueType:
// This might be a structure, an enum, or a delegate
diagnostic = missingType.GetUseSiteInfo().DiagnosticInfo;
break;
}
}
}
}
}
return diagnostic;
}
#nullable enable
internal DiagnosticInfo? GetCompilerFeatureRequiredDiagnostic()
{
var useSiteInfo = GetUseSiteInfo();
if (useSiteInfo.DiagnosticInfo is { Code: (int)ErrorCode.ERR_UnsupportedCompilerFeature } diag)
{
return diag;
}
Debug.Assert(DeriveCompilerFeatureRequiredDiagnostic() is null);
return null;
}
private DiagnosticInfo? DeriveCompilerFeatureRequiredDiagnostic()
{
var decoder = new MetadataDecoder(ContainingPEModule, this);
var diag = PEUtilities.DeriveCompilerFeatureRequiredAttributeDiagnostic(this, ContainingPEModule, Handle, allowedFeatures: IsRefLikeType ? CompilerFeatureRequiredFeatures.RefStructs : CompilerFeatureRequiredFeatures.None, decoder);
if (diag != null)
{
return diag;
}
foreach (var typeParameter in TypeParameters)
{
diag = ((PETypeParameterSymbol)typeParameter).DeriveCompilerFeatureRequiredDiagnostic(decoder);
if (diag != null)
{
return diag;
}
}
return ContainingType is PENamedTypeSymbol containingType
? containingType.GetCompilerFeatureRequiredDiagnostic()
: ContainingPEModule.GetCompilerFeatureRequiredDiagnostic();
}
#nullable disable
internal string DefaultMemberName
{
get
{
var uncommon = GetUncommonProperties();
if (uncommon == s_noUncommonProperties)
{
return "";
}
if (uncommon.lazyDefaultMemberName == null)
{
string defaultMemberName;
this.ContainingPEModule.Module.HasDefaultMemberAttribute(_handle, out defaultMemberName);
// NOTE: the default member name is frequently null (e.g. if there is not indexer in the type).
// Make sure we set a non-null value so that we don't recompute it repeatedly.
// CONSIDER: this makes it impossible to distinguish between not having the attribute and
// having the attribute with a value of "".
Interlocked.CompareExchange(ref uncommon.lazyDefaultMemberName, defaultMemberName ?? "", null);
}
return uncommon.lazyDefaultMemberName;
}
}
internal override bool IsComImport
{
get
{
return (_flags & TypeAttributes.Import) != 0;
}
}
internal override bool ShouldAddWinRTMembers
{
get { return IsWindowsRuntimeImport; }
}
internal override bool IsWindowsRuntimeImport
{
get
{
return (_flags & TypeAttributes.WindowsRuntime) != 0;
}
}
internal override bool GetGuidString(out string guidString)
{
return ContainingPEModule.Module.HasGuidAttribute(_handle, out guidString);
}
internal override TypeLayout Layout
{
get
{
return this.ContainingPEModule.Module.GetTypeLayout(_handle);
}
}
internal override CharSet MarshallingCharSet
{
get
{
CharSet result = _flags.ToCharSet();
if (result == 0)
{
// TODO(tomat): report error
return CharSet.Ansi;
}
return result;
}
}
public override bool IsSerializable
{
#pragma warning disable SYSLIB0050 // 'TypeAttributes.Serializable' is obsolete
get { return (_flags & TypeAttributes.Serializable) != 0; }
#pragma warning restore SYSLIB0050
}
public override bool IsRefLikeType
{
get
{
var uncommon = GetUncommonProperties();
if (uncommon == s_noUncommonProperties)
{
return false;
}
if (!uncommon.lazyIsByRefLike.HasValue())
{
var isByRefLike = ThreeState.False;
if (this.TypeKind == TypeKind.Struct)
{
var moduleSymbol = this.ContainingPEModule;
var module = moduleSymbol.Module;
isByRefLike = module.HasIsByRefLikeAttribute(_handle).ToThreeState();
}
uncommon.lazyIsByRefLike = isByRefLike;
}
return uncommon.lazyIsByRefLike.Value();
}
}
public override bool IsReadOnly
{
get
{
var uncommon = GetUncommonProperties();
if (uncommon == s_noUncommonProperties)
{
return false;
}
if (!uncommon.lazyIsReadOnly.HasValue())
{
var isReadOnly = ThreeState.False;
if (this.TypeKind == TypeKind.Struct)
{
var moduleSymbol = this.ContainingPEModule;
var module = moduleSymbol.Module;
isReadOnly = module.HasIsReadOnlyAttribute(_handle).ToThreeState();
}
uncommon.lazyIsReadOnly = isReadOnly;
}
return uncommon.lazyIsReadOnly.Value();
}
}
internal override bool HasDeclarativeSecurity
{
get { return (_flags & TypeAttributes.HasSecurity) != 0; }
}
internal override IEnumerable<Microsoft.Cci.SecurityAttribute> GetSecurityInformation()
{
throw ExceptionUtilities.Unreachable();
}
internal override NamedTypeSymbol ComImportCoClass
{
get
{
if (!this.IsInterfaceType())
{
return null;
}
var uncommon = GetUncommonProperties();
if (uncommon == s_noUncommonProperties)
{
return null;
}
if (ReferenceEquals(uncommon.lazyComImportCoClassType, ErrorTypeSymbol.UnknownResultType))
{
var type = this.ContainingPEModule.TryDecodeAttributeWithTypeArgument(this.Handle, AttributeDescription.CoClassAttribute);
var coClassType = ((object)type != null && (type.TypeKind == TypeKind.Class || type.IsErrorType())) ? (NamedTypeSymbol)type : null;
Interlocked.CompareExchange(ref uncommon.lazyComImportCoClassType, coClassType, ErrorTypeSymbol.UnknownResultType);
}
return uncommon.lazyComImportCoClassType;
}
}
internal override ImmutableArray<string> GetAppliedConditionalSymbols()
{
var uncommon = GetUncommonProperties();
if (uncommon == s_noUncommonProperties)
{
return ImmutableArray<string>.Empty;
}
if (uncommon.lazyConditionalAttributeSymbols.IsDefault)
{
ImmutableArray<string> conditionalSymbols = this.ContainingPEModule.Module.GetConditionalAttributeValues(_handle);
Debug.Assert(!conditionalSymbols.IsDefault);
ImmutableInterlocked.InterlockedCompareExchange(ref uncommon.lazyConditionalAttributeSymbols, conditionalSymbols, default(ImmutableArray<string>));
}
return uncommon.lazyConditionalAttributeSymbols;
}
internal override ObsoleteAttributeData ObsoleteAttributeData
{
get
{
var uncommon = GetUncommonProperties();
if (uncommon == s_noUncommonProperties)
{
return null;
}
bool ignoreByRefLikeMarker = this.IsRefLikeType;
ObsoleteAttributeHelpers.InitializeObsoleteDataFromMetadata(ref uncommon.lazyObsoleteAttributeData, _handle, ContainingPEModule, ignoreByRefLikeMarker, ignoreRequiredMemberMarker: false);
return uncommon.lazyObsoleteAttributeData;
}
}
internal override AttributeUsageInfo GetAttributeUsageInfo()
{
var uncommon = GetUncommonProperties();
if (uncommon == s_noUncommonProperties)
{
return ((object)this.BaseTypeNoUseSiteDiagnostics != null) ? this.BaseTypeNoUseSiteDiagnostics.GetAttributeUsageInfo() : AttributeUsageInfo.Default;
}
if (uncommon.lazyAttributeUsageInfo.IsNull)
{
uncommon.lazyAttributeUsageInfo = this.DecodeAttributeUsageInfo();
}
return uncommon.lazyAttributeUsageInfo;
}
private AttributeUsageInfo DecodeAttributeUsageInfo()
{
if (this.ContainingPEModule.Module.HasAttributeUsageAttribute(_handle, new MetadataDecoder(ContainingPEModule), out AttributeUsageInfo info))
{
return info.HasValidAttributeTargets ? info : AttributeUsageInfo.Default;
}
return ((object)this.BaseTypeNoUseSiteDiagnostics != null) ? this.BaseTypeNoUseSiteDiagnostics.GetAttributeUsageInfo() : AttributeUsageInfo.Default;
}
internal sealed override CSharpCompilation DeclaringCompilation // perf, not correctness
{
get { return null; }
}
/// <summary>
/// Returns the index of the first member of the specific kind.
/// Returns the number of members if not found.
/// </summary>
private static int GetIndexOfFirstMember(ImmutableArray<Symbol> members, SymbolKind kind)
{
int n = members.Length;
for (int i = 0; i < n; i++)
{
if (members[i].Kind == kind)
{
return i;
}
}
return n;
}
/// <summary>
/// Returns all members of the specific kind, starting at the optional offset.
/// Members of the same kind are assumed to be contiguous.
/// </summary>
private static IEnumerable<TSymbol> GetMembers<TSymbol>(ImmutableArray<Symbol> members, SymbolKind kind, int offset = -1)
where TSymbol : Symbol
{
if (offset < 0)
{
offset = GetIndexOfFirstMember(members, kind);
}
int n = members.Length;
for (int i = offset; i < n; i++)
{
var member = members[i];
if (member.Kind != kind)
{
yield break;
}
yield return (TSymbol)member;
}
}
internal sealed override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls()
{
return SpecializedCollections.EmptyEnumerable<(MethodSymbol Body, MethodSymbol Implemented)>();
}
internal sealed override bool HasInlineArrayAttribute(out int length)
{
if (this.ContainingPEModule.Module.HasInlineArrayAttribute(_handle, out length) && length > 0)
{
return true;
}
length = 0;
return false;
}
#nullable enable
internal sealed override bool HasCollectionBuilderAttribute(out TypeSymbol? builderType, out string? methodName)
{
var uncommon = GetUncommonProperties();
if (uncommon == s_noUncommonProperties)
{
builderType = null;
methodName = null;
return false;
}
if ((object)uncommon.lazyCollectionBuilderAttributeData == CollectionBuilderAttributeData.Uninitialized)
{
Interlocked.CompareExchange(
ref uncommon.lazyCollectionBuilderAttributeData,
getCollectionBuilderAttributeData(),
CollectionBuilderAttributeData.Uninitialized);
}
var attributeData = uncommon.lazyCollectionBuilderAttributeData;
if (attributeData == null)
{
builderType = null;
methodName = null;
return false;
}
builderType = attributeData.BuilderType;
methodName = attributeData.MethodName;
return true;
CollectionBuilderAttributeData? getCollectionBuilderAttributeData()
{
if (ContainingPEModule.Module.HasCollectionBuilderAttribute(_handle, out string builderTypeName, out string methodName))
{
var decoder = new MetadataDecoder(ContainingPEModule);
return new CollectionBuilderAttributeData(decoder.GetTypeSymbolForSerializedType(builderTypeName), methodName);
}
return null;
}
}
internal override bool HasAsyncMethodBuilderAttribute(out TypeSymbol? builderArgument)
{
builderArgument = this.ContainingPEModule.TryDecodeAttributeWithTypeArgument(this.Handle, AttributeDescription.AsyncMethodBuilderAttribute);
return builderArgument is not null;
}
#nullable disable
/// <summary>
/// Specialized PENamedTypeSymbol for types with no type parameters in
/// metadata (no type parameters on this type and all containing types).
/// </summary>
private sealed class PENamedTypeSymbolNonGeneric : PENamedTypeSymbol
{
internal PENamedTypeSymbolNonGeneric(
PEModuleSymbol moduleSymbol,
NamespaceOrTypeSymbol container,
TypeDefinitionHandle handle,
string emittedNamespaceName) :
base(moduleSymbol, container, handle, emittedNamespaceName, 0, out _)
{
}
protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData)
=> throw ExceptionUtilities.Unreachable();
public override int Arity
{
get
{
return 0;
}
}
internal override bool MangleName
{
get
{
return false;
}
}
internal override int MetadataArity
{
get
{
var containingType = _container as PENamedTypeSymbol;
return (object)containingType == null ? 0 : containingType.MetadataArity;
}
}
internal override NamedTypeSymbol AsNativeInteger()
{
Debug.Assert(this.SpecialType == SpecialType.System_IntPtr || this.SpecialType == SpecialType.System_UIntPtr);
if (ContainingAssembly.RuntimeSupportsNumericIntPtr)
{
return this;
}
return ContainingAssembly.GetNativeIntegerType(this);
}
internal override NamedTypeSymbol NativeIntegerUnderlyingType => null;
internal override bool Equals(TypeSymbol t2, TypeCompareKind comparison)
{
return t2 is NativeIntegerTypeSymbol nativeInteger ?
nativeInteger.Equals(this, comparison) :
base.Equals(t2, comparison);
}
}
/// <summary>
/// Specialized PENamedTypeSymbol for types with type parameters in metadata.
/// NOTE: the type may have Arity == 0 if it has same metadata arity as the metadata arity of the containing type.
/// </summary>
private sealed class PENamedTypeSymbolGeneric : PENamedTypeSymbol
{
private readonly GenericParameterHandleCollection _genericParameterHandles;
private readonly ushort _arity;
private readonly bool _mangleName;
private ImmutableArray<TypeParameterSymbol> _lazyTypeParameters;
internal PENamedTypeSymbolGeneric(
PEModuleSymbol moduleSymbol,
NamespaceOrTypeSymbol container,
TypeDefinitionHandle handle,
string emittedNamespaceName,
GenericParameterHandleCollection genericParameterHandles,
ushort arity)
: base(moduleSymbol,
container,
handle,
emittedNamespaceName,
arity,
out bool mangleName)
{
Debug.Assert(genericParameterHandles.Count > 0);
_arity = arity;
if (_arity == 0)
{
_lazyTypeParameters = ImmutableArray<TypeParameterSymbol>.Empty;
}
_genericParameterHandles = genericParameterHandles;
_mangleName = mangleName;
}
protected sealed override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData)
=> throw ExceptionUtilities.Unreachable();
public override int Arity
{
get
{
return _arity;
}
}
internal override bool MangleName
{
get
{
return _mangleName;
}
}
internal override int MetadataArity
{
get
{
return _genericParameterHandles.Count;
}
}
internal override ImmutableArray<TypeWithAnnotations> TypeArgumentsWithAnnotationsNoUseSiteDiagnostics
{
get
{
// This is always the instance type, so the type arguments are the same as the type parameters.
return GetTypeParametersAsTypeArguments();
}
}
public override ImmutableArray<TypeParameterSymbol> TypeParameters
{
get
{
EnsureTypeParametersAreLoaded();
return _lazyTypeParameters;
}
}
internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable();
internal sealed override NamedTypeSymbol NativeIntegerUnderlyingType => null;
private void EnsureTypeParametersAreLoaded()
{
if (_lazyTypeParameters.IsDefault)
{
// If _arity is zero, we should have assigned empty immutable array to _lazyTypeParameters early in the constructor.
Debug.Assert(_arity > 0);
var moduleSymbol = ContainingPEModule;
// If this is a nested type generic parameters in metadata include generic parameters of the outer types.
int firstIndex = _genericParameterHandles.Count - _arity;
var ownedParams = ArrayBuilder<TypeParameterSymbol>.GetInstance(_arity);
ownedParams.Count = _arity;
for (int i = 0; i < ownedParams.Count; i++)
{
ownedParams[i] = new PETypeParameterSymbol(moduleSymbol, this, (ushort)i, _genericParameterHandles[firstIndex + i]);
}
ImmutableInterlocked.InterlockedInitialize(ref _lazyTypeParameters,
ownedParams.ToImmutableAndFree());
}
}
protected override DiagnosticInfo GetUseSiteDiagnosticImpl()
{
DiagnosticInfo diagnostic = null;
if (!MergeUseSiteDiagnostics(ref diagnostic, base.GetUseSiteDiagnosticImpl()))
{
// Verify type parameters for containing types
// match those on the containing types.
if (!MatchesContainingTypeParameters())
{
diagnostic = new CSDiagnosticInfo(ErrorCode.ERR_BogusType, this);
}
}
return diagnostic;
}
/// <summary>
/// Return true if the type parameters specified on the nested type (this),
/// that represent the corresponding type parameters on the containing
/// types, in fact match the actual type parameters on the containing types.
/// </summary>
private bool MatchesContainingTypeParameters()
{
var container = this.ContainingType;
if ((object)container == null)
{
return true;
}
var containingTypeParameters = container.GetAllTypeParameters();
int n = containingTypeParameters.Length;
if (n == 0)
{
return true;
}
// Create an instance of PENamedTypeSymbol for the nested type, but
// with all type parameters, from the nested type and all containing
// types. The type parameters on this temporary type instance are used
// for comparison with those on the actual containing types. The
// containing symbol for the temporary type is the namespace directly.
var nestedType = Create(this.ContainingPEModule, (PENamespaceSymbol)this.ContainingNamespace, _handle, null);
var nestedTypeParameters = nestedType.TypeParameters;
var containingTypeMap = new TypeMap(containingTypeParameters, IndexedTypeParameterSymbol.Take(n), allowAlpha: false);
var nestedTypeMap = new TypeMap(nestedTypeParameters, IndexedTypeParameterSymbol.Take(nestedTypeParameters.Length), allowAlpha: false);
for (int i = 0; i < n; i++)
{
var containingTypeParameter = containingTypeParameters[i];
var nestedTypeParameter = nestedTypeParameters[i];
if (!MemberSignatureComparer.HaveSameConstraints(containingTypeParameter, containingTypeMap, nestedTypeParameter, nestedTypeMap))
{
return false;
}
}
return true;
}
}
}
}
|