Skip to content

Commit

Permalink
Use dictionary lookup while resolving assembly references (#66430)
Browse files Browse the repository at this point in the history
Closes #66410.
  • Loading branch information
AlekseyTs committed Jan 21, 2023
1 parent 7828797 commit ec485d1
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 49 deletions.
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/Symbols/ReferenceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -919,9 +919,9 @@ public override ImmutableArray<AssemblyIdentity> AssemblyReferences
}

public override AssemblyReferenceBinding[] BindAssemblyReferences(
ImmutableArray<AssemblyData> assemblies, AssemblyIdentityComparer assemblyIdentityComparer)
MultiDictionary<string, (AssemblyData DefinitionData, int DefinitionIndex)> assemblies, AssemblyIdentityComparer assemblyIdentityComparer)
{
return ResolveReferencedAssemblies(_referencedAssemblies, assemblies, definitionStartIndex: 0, assemblyIdentityComparer: assemblyIdentityComparer);
return ResolveReferencedAssemblies(_referencedAssemblies, assemblies, resolveAgainstAssemblyBeingBuilt: true, assemblyIdentityComparer: assemblyIdentityComparer);
}

public sealed override bool IsLinked
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ internal static bool IsWindowsComponent(this AssemblyIdentity identity)
identity.Name.StartsWith("windows.", StringComparison.OrdinalIgnoreCase);
}

internal const string WindowsRuntimeIdentitySimpleName = "windows";

// Windows[.winmd]
internal static bool IsWindowsRuntime(this AssemblyIdentity identity)
{
return (identity.ContentType == AssemblyContentType.WindowsRuntime) &&
string.Equals(identity.Name, "windows", StringComparison.OrdinalIgnoreCase);
string.Equals(identity.Name, WindowsRuntimeIdentitySimpleName, StringComparison.OrdinalIgnoreCase);
}
}
}
16 changes: 14 additions & 2 deletions src/Compilers/Core/Portable/ReferenceManager/AssemblyData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis
{
Expand Down Expand Up @@ -48,13 +49,13 @@ internal abstract class AssemblyData
/// In other words, match assembly identities returned by AssemblyReferences property against
/// assemblies described by provided AssemblyData objects.
/// </summary>
/// <param name="assemblies">An array of AssemblyData objects to match against.</param>
/// <param name="assemblies">AssemblyData objects to match against.</param>
/// <param name="assemblyIdentityComparer">Used to compare assembly identities.</param>
/// <returns>
/// For each assembly referenced by this assembly (<see cref="AssemblyReferences"/>)
/// a description of how it binds to one of the input assemblies.
/// </returns>
public abstract AssemblyReferenceBinding[] BindAssemblyReferences(ImmutableArray<AssemblyData> assemblies, AssemblyIdentityComparer assemblyIdentityComparer);
public abstract AssemblyReferenceBinding[] BindAssemblyReferences(MultiDictionary<string, (AssemblyData DefinitionData, int DefinitionIndex)> assemblies, AssemblyIdentityComparer assemblyIdentityComparer);

public abstract bool ContainsNoPiaLocalTypes { get; }

Expand All @@ -69,6 +70,17 @@ internal abstract class AssemblyData
public abstract Compilation? SourceCompilation { get; }

private string GetDebuggerDisplay() => $"{GetType().Name}: [{Identity.GetDisplayName()}]";
#if DEBUG
public sealed override bool Equals(object? obj)
{
return base.Equals(obj);
}

public sealed override int GetHashCode()
{
return base.GetHashCode();
}
#endif
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,27 +75,24 @@ public override IEnumerable<TAssemblySymbol> AvailableSymbols
}

public override AssemblyReferenceBinding[] BindAssemblyReferences(
ImmutableArray<AssemblyData> assemblies,
MultiDictionary<string, (AssemblyData DefinitionData, int DefinitionIndex)> assemblies,
AssemblyIdentityComparer assemblyIdentityComparer)
{
var boundReferences = new AssemblyReferenceBinding[_referencedAssemblies.Length];

for (int i = 0; i < _referencedAssemblyData.Length; i++)
{
Debug.Assert(ReferenceEquals(_referencedAssemblyData[i], assemblies[i + 1]));
boundReferences[i] = new AssemblyReferenceBinding(assemblies[i + 1].Identity, i + 1);
Debug.Assert(assemblies[_referencedAssemblyData[i].Identity.Name].Contains((_referencedAssemblyData[i], i + 1)));
boundReferences[i] = new AssemblyReferenceBinding(_referencedAssemblyData[i].Identity, i + 1);
}

// references from added modules shouldn't resolve against the assembly being built (definition #0)
const int definitionStartIndex = 1;

// resolve references coming from linked modules:
for (int i = _referencedAssemblyData.Length; i < _referencedAssemblies.Length; i++)
{
boundReferences[i] = ResolveReferencedAssembly(
_referencedAssemblies[i],
assemblies,
definitionStartIndex,
resolveAgainstAssemblyBeingBuilt: false, // references from added modules shouldn't resolve against the assembly being built (definition #0)
assemblyIdentityComparer);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ internal partial class CommonReferenceManager<TCompilation, TAssemblySymbol>
///
/// - Result of resolving assembly references of the corresponding assembly
/// against provided set of assembly definitions. Essentially, this is an array returned by
/// <see cref="AssemblyData.BindAssemblyReferences(ImmutableArray{AssemblyData}, AssemblyIdentityComparer)"/> method.
/// <see cref="AssemblyData.BindAssemblyReferences"/> method.
/// </return>
protected BoundInputAssembly[] Bind(
ImmutableArray<AssemblyData> explicitAssemblies,
Expand All @@ -110,17 +110,25 @@ protected BoundInputAssembly[] Bind(
var referenceBindings = ArrayBuilder<AssemblyReferenceBinding[]>.GetInstance();
try
{
var explicitAssembliesMap = new MultiDictionary<string, (AssemblyData DefinitionData, int DefinitionIndex)>(explicitAssemblies.Length, AssemblyIdentityComparer.SimpleNameComparer);

for (int i = 0; i < explicitAssemblies.Length; i++)
{
explicitAssembliesMap.Add(explicitAssemblies[i].Identity.Name, (explicitAssemblies[i], i));
}

// Based on assembly identity, for each assembly,
// bind its references against the other assemblies we have.
for (int i = 0; i < explicitAssemblies.Length; i++)
{
referenceBindings.Add(explicitAssemblies[i].BindAssemblyReferences(explicitAssemblies, IdentityComparer));
referenceBindings.Add(explicitAssemblies[i].BindAssemblyReferences(explicitAssembliesMap, IdentityComparer));
}

if (resolverOpt?.ResolveMissingAssemblies == true)
{
ResolveAndBindMissingAssemblies(
explicitAssemblies,
explicitAssembliesMap,
explicitModules,
explicitReferences,
explicitReferenceMap,
Expand Down Expand Up @@ -191,6 +199,7 @@ protected BoundInputAssembly[] Bind(

private void ResolveAndBindMissingAssemblies(
ImmutableArray<AssemblyData> explicitAssemblies,
MultiDictionary<string, (AssemblyData DefinitionData, int DefinitionIndex)> explicitAssembliesMap,
ImmutableArray<PEModule> explicitModules,
ImmutableArray<MetadataReference> explicitReferences,
ImmutableArray<ResolvedReference> explicitReferenceMap,
Expand Down Expand Up @@ -285,7 +294,7 @@ private void ResolveAndBindMissingAssemblies(
var data = CreateAssemblyDataForResolvedMissingAssembly(resolvedAssemblyMetadata, resolvedReference, importOptions);
implicitAssemblies.Add(data);

var referenceBinding = data.BindAssemblyReferences(explicitAssemblies, IdentityComparer);
var referenceBinding = data.BindAssemblyReferences(explicitAssembliesMap, IdentityComparer);
referenceBindings.Add(referenceBinding);
referenceBindingsToProcess.Push((resolvedReference, new ArraySegment<AssemblyReferenceBinding>(referenceBinding)));
}
Expand All @@ -310,6 +319,15 @@ private void ResolveAndBindMissingAssemblies(
// Rebind assembly references that were initially missing. All bindings established above
// are against explicitly specified references.

// We only need to resolve against implicitly resolved assemblies,
// since we already resolved against explicitly specified ones.
var implicitAssembliesMap = new MultiDictionary<string, (AssemblyData DefinitionData, int DefinitionIndex)>(implicitAssemblies.Count, AssemblyIdentityComparer.SimpleNameComparer);

for (int i = 0; i < implicitAssemblies.Count; i++)
{
implicitAssembliesMap.Add(implicitAssemblies[i].Identity.Name, (implicitAssemblies[i], explicitAssemblyCount + i));
}

allAssemblies = explicitAssemblies.AddRange(implicitAssemblies);

for (int bindingsIndex = 0; bindingsIndex < referenceBindings.Count; bindingsIndex++)
Expand All @@ -332,8 +350,8 @@ private void ResolveAndBindMissingAssemblies(
Debug.Assert(binding.ReferenceIdentity is object);
referenceBinding[i] = ResolveReferencedAssembly(
binding.ReferenceIdentity,
allAssemblies,
explicitAssemblyCount,
implicitAssembliesMap,
resolveAgainstAssemblyBeingBuilt: false,
IdentityComparer);
}
}
Expand Down
Loading

0 comments on commit ec485d1

Please sign in to comment.