| File: Symbols\Metadata\PE\PENamespaceSymbol.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection.Metadata; using System.Threading; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE { /// <summary> /// The base class to represent a namespace imported from a PE/module. Namespaces that differ /// only by casing in name are not merged. /// </summary> internal abstract class PENamespaceSymbol : NamespaceSymbol { /// <summary> /// A map of namespaces immediately contained within this namespace /// mapped by their name (case-sensitively). /// </summary> protected Dictionary<ReadOnlyMemory<char>, PENestedNamespaceSymbol>? lazyNamespaces; /// <summary> /// A map of types immediately contained within this namespace /// grouped by their name (case-sensitively). /// </summary> protected Dictionary<ReadOnlyMemory<char>, ImmutableArray<PENamedTypeSymbol>>? lazyTypes; /// <summary> /// A map of NoPia local types immediately contained in this assembly. /// Maps type name (non-qualified) to the row id. Note, for VB we should use /// full name. /// </summary> private Dictionary<string, TypeDefinitionHandle>? _lazyNoPiaLocalTypes; /// <summary> /// All type members in a flat array /// </summary> private ImmutableArray<PENamedTypeSymbol> _lazyFlattenedTypes; /// <summary> /// All namespace and type members in a flat array /// </summary> private ImmutableArray<Symbol> _lazyFlattenedNamespacesAndTypes; internal sealed override NamespaceExtent Extent { get { return new NamespaceExtent(this.ContainingPEModule); } } [PerformanceSensitive( "https://github.com/dotnet/roslyn/issues/23582", Constraint = "Provide " + nameof(ArrayBuilder<Symbol>) + " capacity to reduce number of allocations.", AllowGenericEnumeration = false)] public sealed override ImmutableArray<Symbol> GetMembers() { if (_lazyFlattenedNamespacesAndTypes.IsDefault) { EnsureAllMembersLoaded(); ImmutableInterlocked.InterlockedExchange(ref _lazyFlattenedNamespacesAndTypes, calculateMembers()); } return _lazyFlattenedNamespacesAndTypes; ImmutableArray<Symbol> calculateMembers() { var memberTypes = GetMemberTypesPrivate(); if (lazyNamespaces.Count == 0) return StaticCast<Symbol>.From(memberTypes); var builder = ArrayBuilder<Symbol>.GetInstance(memberTypes.Length + lazyNamespaces.Count); builder.AddRange(memberTypes); foreach (var pair in lazyNamespaces) { builder.Add(pair.Value); } return builder.ToImmutableAndFree(); } } private ImmutableArray<NamedTypeSymbol> GetMemberTypesPrivate() { //assume that EnsureAllMembersLoaded() has initialize lazyTypes if (_lazyFlattenedTypes.IsDefault) { Debug.Assert(lazyTypes != null); var flattened = lazyTypes.Flatten(); ImmutableInterlocked.InterlockedExchange(ref _lazyFlattenedTypes, flattened); } return StaticCast<NamedTypeSymbol>.From(_lazyFlattenedTypes); } internal override NamespaceSymbol? GetNestedNamespace(ReadOnlyMemory<char> name) { EnsureAllMembersLoaded(); if (lazyNamespaces.TryGetValue(name, out var ns)) { return ns; } return null; } public sealed override ImmutableArray<Symbol> GetMembers(ReadOnlyMemory<char> name) { EnsureAllMembersLoaded(); PENestedNamespaceSymbol? ns = null; ImmutableArray<PENamedTypeSymbol> t; if (lazyNamespaces.TryGetValue(name, out ns)) { if (lazyTypes.TryGetValue(name, out t)) { // TODO - Eliminate the copy by storing all members and type members instead of non-type and type members? return StaticCast<Symbol>.From(t).Add(ns); } else { return ImmutableArray.Create<Symbol>(ns); } } else if (lazyTypes.TryGetValue(name, out t)) { return StaticCast<Symbol>.From(t); } return ImmutableArray<Symbol>.Empty; } public sealed override ImmutableArray<NamedTypeSymbol> GetTypeMembers() { EnsureAllMembersLoaded(); return GetMemberTypesPrivate(); } public sealed override ImmutableArray<NamedTypeSymbol> GetTypeMembers(ReadOnlyMemory<char> name) { EnsureAllMembersLoaded(); ImmutableArray<PENamedTypeSymbol> t; return lazyTypes.TryGetValue(name, out t) ? StaticCast<NamedTypeSymbol>.From(t) : ImmutableArray<NamedTypeSymbol>.Empty; } public sealed override ImmutableArray<NamedTypeSymbol> GetTypeMembers(ReadOnlyMemory<char> name, int arity) { return GetTypeMembers(name).WhereAsArray((type, arity) => type.Arity == arity, arity); } public sealed override ImmutableArray<Location> Locations { get { return ContainingPEModule.MetadataLocation.Cast<MetadataLocation, Location>(); } } public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences { get { return ImmutableArray<SyntaxReference>.Empty; } } /// <summary> /// Returns PEModuleSymbol containing the namespace. /// </summary> /// <returns>PEModuleSymbol containing the namespace.</returns> internal abstract PEModuleSymbol ContainingPEModule { get; } [MemberNotNull(nameof(lazyTypes))] [MemberNotNull(nameof(lazyNamespaces))] protected abstract void EnsureAllMembersLoaded(); /// <summary> /// Initializes namespaces and types maps with information about /// namespaces and types immediately contained within this namespace. /// </summary> /// <param name="typesByNS"> /// The sequence of groups of TypeDef row ids for types contained within the namespace, /// recursively including those from nested namespaces. The row ids must be grouped by the /// fully-qualified namespace name case-sensitively. There could be multiple groups /// for each fully-qualified namespace name. The groups must be sorted by /// their key in case-sensitive manner. Empty string must be used as namespace name for types /// immediately contained within Global namespace. Therefore, all types in this namespace, if any, /// must be in several first IGroupings. /// </param> [MemberNotNull(nameof(lazyTypes))] [MemberNotNull(nameof(lazyNamespaces))] protected void LoadAllMembers(IEnumerable<IGrouping<string, TypeDefinitionHandle>> typesByNS) { Debug.Assert(typesByNS != null); // A sequence of groups of TypeDef row ids for types immediately contained within this namespace. IEnumerable<IGrouping<string, TypeDefinitionHandle>>? nestedTypes = null; // A sequence with information about namespaces immediately contained within this namespace. // For each pair: // Key - contains simple name of a child namespace. // Value - contains a sequence similar to the one passed to this function, but // calculated for the child namespace. IEnumerable<KeyValuePair<string, IEnumerable<IGrouping<string, TypeDefinitionHandle>>>>? nestedNamespaces = null; bool isGlobalNamespace = this.IsGlobalNamespace; MetadataHelpers.GetInfoForImmediateNamespaceMembers( isGlobalNamespace, isGlobalNamespace ? 0 : GetQualifiedNameLength(), typesByNS, StringComparer.Ordinal, out nestedTypes, out nestedNamespaces); LazyInitializeNamespaces(nestedNamespaces); LazyInitializeTypes(nestedTypes); } private int GetQualifiedNameLength() { int length = this.Name.Length; var parent = ContainingNamespace; while (parent?.IsGlobalNamespace == false) { // add name of the parent + "." length += parent.Name.Length + 1; parent = parent.ContainingNamespace; } return length; } /// <summary> /// Create symbols for nested namespaces and initialize namespaces map. /// </summary> [MemberNotNull(nameof(lazyNamespaces))] private void LazyInitializeNamespaces( IEnumerable<KeyValuePair<string, IEnumerable<IGrouping<string, TypeDefinitionHandle>>>> childNamespaces) { if (this.lazyNamespaces == null) { var namespaces = new Dictionary<ReadOnlyMemory<char>, PENestedNamespaceSymbol>(ReadOnlyMemoryOfCharComparer.Instance); foreach (var child in childNamespaces) { var c = new PENestedNamespaceSymbol(child.Key, this, child.Value); namespaces.Add(c.Name.AsMemory(), c); } Interlocked.CompareExchange(ref this.lazyNamespaces, namespaces, null); } } /// <summary> /// Create symbols for nested types and initialize types map. /// </summary> [MemberNotNull(nameof(lazyTypes))] private void LazyInitializeTypes(IEnumerable<IGrouping<string, TypeDefinitionHandle>> typeGroups) { if (this.lazyTypes == null) { var moduleSymbol = ContainingPEModule; var children = ArrayBuilder<PENamedTypeSymbol>.GetInstance(); var skipCheckForPiaType = !moduleSymbol.Module.ContainsNoPiaLocalTypes(); Dictionary<string, TypeDefinitionHandle>? noPiaLocalTypes = null; foreach (var g in typeGroups) { foreach (var t in g) { if (skipCheckForPiaType || !moduleSymbol.Module.IsNoPiaLocalType(t)) { children.Add(PENamedTypeSymbol.Create(moduleSymbol, this, t, g.Key)); } else { try { string typeDefName = moduleSymbol.Module.GetTypeDefNameOrThrow(t); if (noPiaLocalTypes == null) { noPiaLocalTypes = new Dictionary<string, TypeDefinitionHandle>(StringOrdinalComparer.Instance); } noPiaLocalTypes[typeDefName] = t; } catch (BadImageFormatException) { } } } } var typesDict = children.ToDictionary(c => c.Name.AsMemory(), ReadOnlyMemoryOfCharComparer.Instance); children.Free(); if (noPiaLocalTypes != null) { Interlocked.CompareExchange(ref _lazyNoPiaLocalTypes, noPiaLocalTypes, null); } var original = Interlocked.CompareExchange(ref this.lazyTypes, typesDict, null); // Build cache of TypeDef Tokens // Potentially this can be done in the background. if (original == null) { moduleSymbol.OnNewTypeDeclarationsLoaded(typesDict); } } } internal NamedTypeSymbol? UnifyIfNoPiaLocalType(ref MetadataTypeName emittedTypeName) { EnsureAllMembersLoaded(); TypeDefinitionHandle typeDef; // See if this is a NoPia local type, which we should unify. // Note, VB should use FullName. if (_lazyNoPiaLocalTypes != null && _lazyNoPiaLocalTypes.TryGetValue(emittedTypeName.TypeName, out typeDef)) { var result = (NamedTypeSymbol)new MetadataDecoder(ContainingPEModule).GetTypeOfToken(typeDef, out bool isNoPiaLocalType); Debug.Assert(isNoPiaLocalType); Debug.Assert(result is not null); return result; } return null; } } }