Skip to content

Commit

Permalink
Share common code involved into building dictionaries of members for …
Browse files Browse the repository at this point in the history
…namespace symbols (#67933)
  • Loading branch information
AlekseyTs authored Apr 26, 2023
1 parent bc609ca commit 7c23c2e
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 247 deletions.
135 changes: 12 additions & 123 deletions src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -249,58 +249,19 @@ private Dictionary<string, ImmutableArray<NamedTypeSymbol>> 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<string, ImmutableArray<NamedTypeSymbol>> GetTypesFromMemberMap(Dictionary<string, ImmutableArray<NamespaceOrTypeSymbol>> map)
{
var dictionary = new Dictionary<string, ImmutableArray<NamedTypeSymbol>>(StringOrdinalComparer.Instance);

foreach (var kvp in map)
static Dictionary<string, ImmutableArray<NamedTypeSymbol>> getTypesFromMemberMap(Dictionary<string, ImmutableArray<NamespaceOrTypeSymbol>> map)
{
ImmutableArray<NamespaceOrTypeSymbol> 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<NamedTypeSymbol>().AsImmutable());
}
else
{
dictionary.Add(kvp.Key, members.As<NamedTypeSymbol>());
}
}
#if DEBUG
return ImmutableArrayExtensions.GetTypesFromMemberMap<NamespaceOrTypeSymbol, NamedTypeSymbol, NamespaceSymbol>(map, StringOrdinalComparer.Instance);
#else
return ImmutableArrayExtensions.GetTypesFromMemberMap<NamespaceOrTypeSymbol, NamedTypeSymbol>(map, StringOrdinalComparer.Instance);
#endif
}

return dictionary;
}

private Dictionary<string, ImmutableArray<NamespaceOrTypeSymbol>> MakeNameToMembersMap(BindingDiagnosticBag diagnostics)
Expand All @@ -314,13 +275,15 @@ private Dictionary<string, ImmutableArray<NamespaceOrTypeSymbol>> 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<NamedTypeSymbol>

var builder = new NameToSymbolMapBuilder();
var builder = PooledDictionary<string, object>.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<string, ImmutableArray<NamespaceOrTypeSymbol>>(builder.Count);
ImmutableArrayExtensions.CreateNameToMembersMap<NamespaceOrTypeSymbol, NamedTypeSymbol, NamespaceSymbol>(builder, result);
builder.Free();

CheckMembers(this, result, diagnostics);
Expand Down Expand Up @@ -514,79 +477,5 @@ private void RegisterDeclaredCorTypes()

return false;
}

private readonly struct NameToSymbolMapBuilder
{
private readonly PooledDictionary<string, object> _dictionary = PooledDictionary<string, object>.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<NamespaceOrTypeSymbol>;
if (builder == null)
{
builder = ArrayBuilder<NamespaceOrTypeSymbol>.GetInstance();
builder.Add((NamespaceOrTypeSymbol)item);
_dictionary[name] = builder;
}
builder.Add(symbol);
}
else
{
_dictionary[name] = symbol;
}
}

public Dictionary<String, ImmutableArray<NamespaceOrTypeSymbol>> CreateMap()
{
var result = new Dictionary<String, ImmutableArray<NamespaceOrTypeSymbol>>(_dictionary.Count);

foreach (var kvp in _dictionary)
{
object value = kvp.Value;
ImmutableArray<NamespaceOrTypeSymbol> members;

var builder = value as ArrayBuilder<NamespaceOrTypeSymbol>;
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<NamespaceOrTypeSymbol>.From(builder.ToDowncastedImmutable<NamedTypeSymbol>());

builder.Free();
}
else
{
NamespaceOrTypeSymbol symbol = (NamespaceOrTypeSymbol)value;
members = symbol.Kind == SymbolKind.Namespace
? ImmutableArray.Create<NamespaceOrTypeSymbol>(symbol)
: StaticCast<NamespaceOrTypeSymbol>.From(ImmutableArray.Create<NamedTypeSymbol>((NamedTypeSymbol)symbol));
}

result.Add(kvp.Key, members);
}

return result;
}
}
}
}
154 changes: 133 additions & 21 deletions src/Compilers/Core/Portable/Collections/ImmutableArrayExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -860,27 +860,7 @@ internal static Dictionary<K, ImmutableArray<T>> ToDictionary<K, T>(this Immutab
foreach (var item in items)
{
var key = keySelector(item);
if (accumulator.TryGetValue(key, out var existingValueOrArray))
{
if (existingValueOrArray is ArrayBuilder<T> 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<T>.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<K, ImmutableArray<T>>(accumulator.Count);
Expand All @@ -898,6 +878,138 @@ internal static Dictionary<K, ImmutableArray<T>> ToDictionary<K, T>(this Immutab
return dictionary;
}

internal static void AddToMultiValueDictionaryBuilder<K, T>(Dictionary<K, object> accumulator, K key, T item)
where K : notnull
where T : notnull
{
if (accumulator.TryGetValue(key, out var existingValueOrArray))
{
if (existingValueOrArray is ArrayBuilder<T> 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<T>.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
<TNamespaceOrTypeSymbol, TNamedTypeSymbol, TNamespaceSymbol>
(Dictionary<string, object> dictionary, Dictionary<String, ImmutableArray<TNamespaceOrTypeSymbol>> result)
where TNamespaceOrTypeSymbol : class
where TNamedTypeSymbol : class, TNamespaceOrTypeSymbol
where TNamespaceSymbol : class, TNamespaceOrTypeSymbol
{
foreach (var kvp in dictionary)
{
object value = kvp.Value;
ImmutableArray<TNamespaceOrTypeSymbol> members;

var builder = value as ArrayBuilder<TNamespaceOrTypeSymbol>;
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<TNamespaceOrTypeSymbol>.CastUp(builder.ToDowncastedImmutable<TNamedTypeSymbol>());

builder.Free();
}
else
{
TNamespaceOrTypeSymbol symbol = (TNamespaceOrTypeSymbol)value;
members = symbol is TNamespaceSymbol
? ImmutableArray.Create<TNamespaceOrTypeSymbol>(symbol)
: ImmutableArray<TNamespaceOrTypeSymbol>.CastUp(ImmutableArray.Create<TNamedTypeSymbol>((TNamedTypeSymbol)symbol));
}

result.Add(kvp.Key, members);
}
}

internal static Dictionary<string, ImmutableArray<TNamedTypeSymbol>> GetTypesFromMemberMap
<TNamespaceOrTypeSymbol, TNamedTypeSymbol
#if DEBUG
, TNamespaceSymbol
#endif
>
(Dictionary<string, ImmutableArray<TNamespaceOrTypeSymbol>> map, IEqualityComparer<string> comparer)
where TNamespaceOrTypeSymbol : class
where TNamedTypeSymbol : class, TNamespaceOrTypeSymbol
#if DEBUG
where TNamespaceSymbol : class, TNamespaceOrTypeSymbol
#endif
{
var dictionary = new Dictionary<string, ImmutableArray<TNamedTypeSymbol>>(comparer);

foreach (var kvp in map)
{
ImmutableArray<TNamespaceOrTypeSymbol> 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<TNamedTypeSymbol>().AsImmutable());
}
else
{
dictionary.Add(kvp.Key, members.As<TNamedTypeSymbol>());
}
}
}

return dictionary;
}

internal static bool SequenceEqual<TElement, TArg>(this ImmutableArray<TElement> array1, ImmutableArray<TElement> array2, TArg arg, Func<TElement, TElement, TArg, bool> predicate)
{
// The framework implementation of SequenceEqual forces a NullRef for default array1 and 2, so we
Expand Down
Loading

0 comments on commit 7c23c2e

Please sign in to comment.