From 7c23c2ec0da28827077fc710efeac97c07ec443b Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Wed, 26 Apr 2023 07:50:19 -0700 Subject: [PATCH] Share common code involved into building dictionaries of members for namespace symbols (#67933) --- .../Symbols/Source/SourceNamespaceSymbol.cs | 135 ++------------- .../Collections/ImmutableArrayExtensions.cs | 154 +++++++++++++++--- .../Symbols/Source/SourceNamespaceSymbol.vb | 115 ++----------- 3 files changed, 157 insertions(+), 247 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs index a8072e38d45d4..c30aa9fee220e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs @@ -249,58 +249,19 @@ private Dictionary> GetNameToTypeMembers { // NOTE: This method depends on MakeNameToMembersMap() on creating a proper // NOTE: type of the array, see comments in MakeNameToMembersMap() for details - Interlocked.CompareExchange(ref _nameToTypeMembersMap, GetTypesFromMemberMap(GetNameToMembersMap()), null); + Interlocked.CompareExchange(ref _nameToTypeMembersMap, getTypesFromMemberMap(GetNameToMembersMap()), null); } return _nameToTypeMembersMap; - } - - private static Dictionary> GetTypesFromMemberMap(Dictionary> map) - { - var dictionary = new Dictionary>(StringOrdinalComparer.Instance); - foreach (var kvp in map) + static Dictionary> getTypesFromMemberMap(Dictionary> map) { - ImmutableArray members = kvp.Value; - - bool hasType = false; - bool hasNamespace = false; - - foreach (var symbol in members) - { - if (symbol.Kind == SymbolKind.NamedType) - { - hasType = true; - if (hasNamespace) - { - break; - } - } - else - { - Debug.Assert(symbol.Kind == SymbolKind.Namespace); - hasNamespace = true; - if (hasType) - { - break; - } - } - } - - if (hasType) - { - if (hasNamespace) - { - dictionary.Add(kvp.Key, members.OfType().AsImmutable()); - } - else - { - dictionary.Add(kvp.Key, members.As()); - } - } +#if DEBUG + return ImmutableArrayExtensions.GetTypesFromMemberMap(map, StringOrdinalComparer.Instance); +#else + return ImmutableArrayExtensions.GetTypesFromMemberMap(map, StringOrdinalComparer.Instance); +#endif } - - return dictionary; } private Dictionary> MakeNameToMembersMap(BindingDiagnosticBag diagnostics) @@ -314,13 +275,15 @@ private Dictionary> MakeNameToMemb // NOTE: a name maps into values collection containing types only instead of allocating another // NOTE: array of NamedTypeSymbol[] we downcast the array to ImmutableArray - var builder = new NameToSymbolMapBuilder(); + var builder = PooledDictionary.GetInstance(); foreach (var declaration in _mergedDeclaration.Children) { - builder.Add(BuildSymbol(declaration, diagnostics)); + NamespaceOrTypeSymbol symbol = BuildSymbol(declaration, diagnostics); + ImmutableArrayExtensions.AddToMultiValueDictionaryBuilder(builder, symbol.Name, symbol); } - var result = builder.CreateMap(); + var result = new Dictionary>(builder.Count); + ImmutableArrayExtensions.CreateNameToMembersMap(builder, result); builder.Free(); CheckMembers(this, result, diagnostics); @@ -514,79 +477,5 @@ private void RegisterDeclaredCorTypes() return false; } - - private readonly struct NameToSymbolMapBuilder - { - private readonly PooledDictionary _dictionary = PooledDictionary.GetInstance(); - - public NameToSymbolMapBuilder() - { - } - - public void Free() - { - _dictionary.Free(); - } - - public void Add(NamespaceOrTypeSymbol symbol) - { - string name = symbol.Name; - object item; - if (_dictionary.TryGetValue(name, out item)) - { - var builder = item as ArrayBuilder; - if (builder == null) - { - builder = ArrayBuilder.GetInstance(); - builder.Add((NamespaceOrTypeSymbol)item); - _dictionary[name] = builder; - } - builder.Add(symbol); - } - else - { - _dictionary[name] = symbol; - } - } - - public Dictionary> CreateMap() - { - var result = new Dictionary>(_dictionary.Count); - - foreach (var kvp in _dictionary) - { - object value = kvp.Value; - ImmutableArray members; - - var builder = value as ArrayBuilder; - if (builder != null) - { - Debug.Assert(builder.Count > 1); - bool hasNamespaces = false; - for (int i = 0; (i < builder.Count) && !hasNamespaces; i++) - { - hasNamespaces |= (builder[i].Kind == SymbolKind.Namespace); - } - - members = hasNamespaces - ? builder.ToImmutable() - : StaticCast.From(builder.ToDowncastedImmutable()); - - builder.Free(); - } - else - { - NamespaceOrTypeSymbol symbol = (NamespaceOrTypeSymbol)value; - members = symbol.Kind == SymbolKind.Namespace - ? ImmutableArray.Create(symbol) - : StaticCast.From(ImmutableArray.Create((NamedTypeSymbol)symbol)); - } - - result.Add(kvp.Key, members); - } - - return result; - } - } } } diff --git a/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs b/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs index 8e614a6e6e722..800dea2681bc9 100644 --- a/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs +++ b/src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs @@ -860,27 +860,7 @@ internal static Dictionary> ToDictionary(this Immutab foreach (var item in items) { var key = keySelector(item); - if (accumulator.TryGetValue(key, out var existingValueOrArray)) - { - if (existingValueOrArray is ArrayBuilder arrayBuilder) - { - // Already a builder in the accumulator, just add to that. - arrayBuilder.Add(item); - } - else - { - // Just a single value in the accumulator so far. Convert to using a builder. - arrayBuilder = ArrayBuilder.GetInstance(capacity: 2); - arrayBuilder.Add((T)existingValueOrArray); - arrayBuilder.Add(item); - accumulator[key] = arrayBuilder; - } - } - else - { - // Nothing in the dictionary so far. Add the item directly. - accumulator.Add(key, item); - } + AddToMultiValueDictionaryBuilder(accumulator, key, item); } var dictionary = new Dictionary>(accumulator.Count); @@ -898,6 +878,138 @@ internal static Dictionary> ToDictionary(this Immutab return dictionary; } + internal static void AddToMultiValueDictionaryBuilder(Dictionary accumulator, K key, T item) + where K : notnull + where T : notnull + { + if (accumulator.TryGetValue(key, out var existingValueOrArray)) + { + if (existingValueOrArray is ArrayBuilder arrayBuilder) + { + // Already a builder in the accumulator, just add to that. + } + else + { + // Just a single value in the accumulator so far. Convert to using a builder. + arrayBuilder = ArrayBuilder.GetInstance(capacity: 2); + arrayBuilder.Add((T)existingValueOrArray); + accumulator[key] = arrayBuilder; + } + + arrayBuilder.Add(item); + } + else + { + // Nothing in the dictionary so far. Add the item directly. + accumulator.Add(key, item); + } + } + + internal static void CreateNameToMembersMap + + (Dictionary dictionary, Dictionary> result) + where TNamespaceOrTypeSymbol : class + where TNamedTypeSymbol : class, TNamespaceOrTypeSymbol + where TNamespaceSymbol : class, TNamespaceOrTypeSymbol + { + foreach (var kvp in dictionary) + { + object value = kvp.Value; + ImmutableArray members; + + var builder = value as ArrayBuilder; + if (builder != null) + { + Debug.Assert(builder.Count > 1); + bool hasNamespaces = false; + for (int i = 0; i < builder.Count; i++) + { + if (builder[i] is TNamespaceSymbol) + { + hasNamespaces = true; + break; + } + } + + members = hasNamespaces + ? builder.ToImmutable() + : ImmutableArray.CastUp(builder.ToDowncastedImmutable()); + + builder.Free(); + } + else + { + TNamespaceOrTypeSymbol symbol = (TNamespaceOrTypeSymbol)value; + members = symbol is TNamespaceSymbol + ? ImmutableArray.Create(symbol) + : ImmutableArray.CastUp(ImmutableArray.Create((TNamedTypeSymbol)symbol)); + } + + result.Add(kvp.Key, members); + } + } + + internal static Dictionary> GetTypesFromMemberMap + + (Dictionary> map, IEqualityComparer comparer) + where TNamespaceOrTypeSymbol : class + where TNamedTypeSymbol : class, TNamespaceOrTypeSymbol +#if DEBUG + where TNamespaceSymbol : class, TNamespaceOrTypeSymbol +#endif + { + var dictionary = new Dictionary>(comparer); + + foreach (var kvp in map) + { + ImmutableArray members = kvp.Value; + + bool hasType = false; + bool hasNamespace = false; + + foreach (var symbol in members) + { + if (symbol is TNamedTypeSymbol) + { + hasType = true; + if (hasNamespace) + { + break; + } + } + else + { +#if DEBUG + Debug.Assert(symbol is TNamespaceSymbol); +#endif + hasNamespace = true; + if (hasType) + { + break; + } + } + } + + if (hasType) + { + if (hasNamespace) + { + dictionary.Add(kvp.Key, members.OfType().AsImmutable()); + } + else + { + dictionary.Add(kvp.Key, members.As()); + } + } + } + + return dictionary; + } + internal static bool SequenceEqual(this ImmutableArray array1, ImmutableArray array2, TArg arg, Func predicate) { // The framework implementation of SequenceEqual forces a NullRef for default array1 and 2, so we diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceNamespaceSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceNamespaceSymbol.vb index f8df67181798d..1c28810875189 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceNamespaceSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceNamespaceSymbol.vb @@ -143,86 +143,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ' NOTE: a name maps into values collection containing types only instead of allocating another ' NOTE: array of NamedTypeSymbol[] we downcast the array to ImmutableArray(Of NamedTypeSymbol) - Dim builder As New NameToSymbolMapBuilder(_declaration.Children.Length) + Dim builder As New Dictionary(Of String, Object)(_declaration.Children.Length, IdentifierComparison.Comparer) For Each declaration In _declaration.Children - builder.Add(BuildSymbol(declaration)) + Dim symbol As NamespaceOrTypeSymbol = BuildSymbol(declaration) + ImmutableArrayExtensions.AddToMultiValueDictionaryBuilder(builder, symbol.Name, symbol) Next ' TODO(cyrusn): The C# and VB impls differ here. C# reports errors here and VB does not. ' Is that what we want? - Return builder.CreateMap() + Dim result As New Dictionary(Of String, ImmutableArray(Of NamespaceOrTypeSymbol))(builder.Count, IdentifierComparison.Comparer) + ImmutableArrayExtensions.CreateNameToMembersMap(Of NamespaceOrTypeSymbol, NamedTypeSymbol, NamespaceSymbol)(builder, result) + Return result End Function - Private Structure NameToSymbolMapBuilder - Private ReadOnly _dictionary As Dictionary(Of String, Object) - - Public Sub New(capacity As Integer) - _dictionary = New Dictionary(Of String, Object)(capacity, IdentifierComparison.Comparer) - End Sub - - Public Sub Add(symbol As NamespaceOrTypeSymbol) - Dim name As String = symbol.Name - Dim item As Object = Nothing - - If Me._dictionary.TryGetValue(name, item) Then - Dim builder = TryCast(item, ArrayBuilder(Of NamespaceOrTypeSymbol)) - If builder Is Nothing Then - builder = ArrayBuilder(Of NamespaceOrTypeSymbol).GetInstance() - builder.Add(DirectCast(item, NamespaceOrTypeSymbol)) - Me._dictionary(name) = builder - End If - builder.Add(symbol) - - Else - Me._dictionary(name) = symbol - End If - - End Sub - - Public Function CreateMap() As Dictionary(Of String, ImmutableArray(Of NamespaceOrTypeSymbol)) - Dim result As New Dictionary(Of String, ImmutableArray(Of NamespaceOrTypeSymbol))(Me._dictionary.Count, IdentifierComparison.Comparer) - - For Each kvp In Me._dictionary - - Dim value As Object = kvp.Value - Dim members As ImmutableArray(Of NamespaceOrTypeSymbol) - - Dim builder = TryCast(value, ArrayBuilder(Of NamespaceOrTypeSymbol)) - If builder IsNot Nothing Then - Debug.Assert(builder.Count > 1) - Dim hasNamespaces As Boolean = False - - For i = 0 To builder.Count - 1 - If builder(i).Kind = SymbolKind.Namespace Then - hasNamespaces = True - Exit For - End If - Next - - If hasNamespaces Then - members = builder.ToImmutable() - Else - members = StaticCast(Of NamespaceOrTypeSymbol).From(builder.ToDowncastedImmutable(Of NamedTypeSymbol)()) - End If - - builder.Free() - Else - Dim symbol = DirectCast(value, NamespaceOrTypeSymbol) - If symbol.Kind = SymbolKind.Namespace Then - members = ImmutableArray.Create(Of NamespaceOrTypeSymbol)(symbol) - Else - members = StaticCast(Of NamespaceOrTypeSymbol).From(ImmutableArray.Create(Of NamedTypeSymbol)(DirectCast(symbol, NamedTypeSymbol))) - End If - End If - - result.Add(kvp.Key, members) - Next - - Return result - End Function - End Structure - Private Function BuildSymbol(decl As MergedNamespaceOrTypeDeclaration) As NamespaceOrTypeSymbol Dim namespaceDecl = TryCast(decl, MergedNamespaceDeclaration) If namespaceDecl IsNot Nothing Then @@ -253,39 +187,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ' NOTE: This method depends on MakeNameToMembersMap() on creating a proper ' NOTE: type of the array, see comments in MakeNameToMembersMap() for details - Dim dictionary As New Dictionary(Of String, ImmutableArray(Of NamedTypeSymbol))(CaseInsensitiveComparison.Comparer) - + Dim dictionary As New Dictionary(Of String, ImmutableArray(Of NamedTypeSymbol)) Dim map As Dictionary(Of String, ImmutableArray(Of NamespaceOrTypeSymbol)) = Me.GetNameToMembersMap() - For Each kvp In map - Dim members As ImmutableArray(Of NamespaceOrTypeSymbol) = kvp.Value - - Dim hasType As Boolean = False - Dim hasNamespace As Boolean = False - - For Each symbol In members - If symbol.Kind = SymbolKind.NamedType Then - hasType = True - If hasNamespace Then - Exit For - End If - Else - Debug.Assert(symbol.Kind = SymbolKind.Namespace) - hasNamespace = True - If hasType Then - Exit For - End If - End If - Next - - If hasType Then - If hasNamespace Then - dictionary.Add(kvp.Key, members.OfType(Of NamedTypeSymbol).AsImmutable()) - Else - dictionary.Add(kvp.Key, members.As(Of NamedTypeSymbol)) - End If - End If - Next +#If DEBUG Then + dictionary = ImmutableArrayExtensions.GetTypesFromMemberMap(Of NamespaceOrTypeSymbol, NamedTypeSymbol, NamespaceSymbol)(map, CaseInsensitiveComparison.Comparer) +#Else + dictionary = ImmutableArrayExtensions.GetTypesFromMemberMap(Of NamespaceOrTypeSymbol, NamedTypeSymbol)(map, CaseInsensitiveComparison.Comparer) +#End If Interlocked.CompareExchange(_nameToTypeMembersMap, dictionary, Nothing) End If