提交 bb38549f 编写于 作者: T Tomas Matousek

Missing assembly resolution

上级 2cb417c3
......@@ -2174,5 +2174,310 @@ public void AsymmetricUnification()
Assert.Equal("System.Numerics.Vectors, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", ((IAssemblySymbol)a0).Identity.GetDisplayName());
Assert.Equal("System.Numerics.Vectors, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", ((IAssemblySymbol)a1).Identity.GetDisplayName());
}
private class TestMissingMetadataReferenceResolver : MetadataReferenceResolver
{
private readonly Dictionary<string, MetadataReference> _map;
public readonly List<AssemblyIdentity> ResolvedIdentities = new List<AssemblyIdentity>();
public TestMissingMetadataReferenceResolver(Dictionary<string, MetadataReference> map)
{
_map = map;
}
public override PortableExecutableReference ResolveMissingAssembly(AssemblyIdentity identity)
{
ResolvedIdentities.Add(identity);
MetadataReference reference;
string nameAndVersion = identity.Name + (identity.Version != AssemblyIdentity.NullVersion ? $", {identity.Version}" : "");
return _map.TryGetValue(nameAndVersion, out reference) ? (PortableExecutableReference)reference : null;
}
public override bool ResolveMissingAssemblies => true;
public override bool Equals(object other) => true;
public override int GetHashCode() => 1;
public override ImmutableArray<PortableExecutableReference> ResolveReference(string reference, string baseFilePath, MetadataReferenceProperties properties) => default(ImmutableArray<PortableExecutableReference>);
}
[Fact]
public void MissingAssemblyResolution1()
{
// c - a -> b
var bRef = CreateCompilationWithMscorlib("public class B { }", assemblyName: "B").EmitToImageReference();
var aRef = CreateCompilationWithMscorlib("public class A : B { }", new[] { bRef }, assemblyName: "A").EmitToImageReference();
var c = CreateCompilationWithMscorlib("public class C : A { }", new[] { aRef },
TestOptions.ReleaseDll.WithMetadataReferenceResolver(new TestMissingMetadataReferenceResolver(new Dictionary<string, MetadataReference>
{
{ "B", bRef }
})));
c.VerifyEmitDiagnostics();
Assert.Equal("B", ((AssemblySymbol)c.GetAssemblyOrModuleSymbol(bRef)).Name);
}
[Fact]
public void MissingAssemblyResolution_Aliases()
{
// c - a -> b with alias X
var bRef = CreateCompilationWithMscorlib("public class B { }", assemblyName: "B").EmitToImageReference();
var aRef = CreateCompilationWithMscorlib("public class A : B { }", new[] { bRef }, assemblyName: "A").EmitToImageReference();
var c = CreateCompilationWithMscorlib(@"
extern alias X;
public class C : A
{
X::B F() => null;
}
", new[] { aRef },
TestOptions.ReleaseDll.WithMetadataReferenceResolver(new TestMissingMetadataReferenceResolver(new Dictionary<string, MetadataReference>
{
{ "B", bRef.WithAliases(ImmutableArray.Create("X")) }
})));
c.VerifyEmitDiagnostics();
}
[Fact]
public void MissingAssemblyResolution_None()
{
// c - a -> d
// - d
var dRef = CreateCompilationWithMscorlib("public interface D { }", assemblyName: "D").EmitToImageReference();
var aRef = CreateCompilationWithMscorlib("public interface A : D { }", new[] { dRef }, assemblyName: "A").ToMetadataReference();
var resolver = new TestMissingMetadataReferenceResolver(new Dictionary<string, MetadataReference>());
var c = CreateCompilationWithMscorlib("public interface C : A { }", new[] { aRef, dRef },
TestOptions.ReleaseDll.WithMetadataReferenceResolver(resolver));
c.VerifyDiagnostics();
Assert.Equal(0, resolver.ResolvedIdentities.Count);
}
[Fact]
public void MissingAssemblyResolution_Multiple()
{
// c - a -> d
// - b -> d
var dRef = CreateCompilationWithMscorlib("public interface D { }", assemblyName: "D").EmitToImageReference();
var aRef = CreateCompilationWithMscorlib("public interface A : D { }", new[] { dRef }, assemblyName: "A").ToMetadataReference();
var bRef = CreateCompilationWithMscorlib("public interface B : D { }", new[] { dRef }, assemblyName: "B").ToMetadataReference();
var resolver = new TestMissingMetadataReferenceResolver(new Dictionary<string, MetadataReference>
{
{ "D", dRef }
});
var c = CreateCompilationWithMscorlib("public interface C : A, B { }", new[] { aRef, bRef },
TestOptions.ReleaseDll.WithMetadataReferenceResolver(resolver));
c.VerifyEmitDiagnostics();
Assert.Equal("D", ((AssemblySymbol)c.GetAssemblyOrModuleSymbol(dRef)).Name);
Assert.Equal(1, resolver.ResolvedIdentities.Count);
}
[Fact]
public void MissingAssemblyResolution_Modules()
{
// c - a - d
// - module(m) - b
// - module(n) - d
var bRef = CreateCompilationWithMscorlib("public interface B { }", assemblyName: "B").EmitToImageReference();
var dRef = CreateCompilationWithMscorlib("public interface D { }", assemblyName: "D").EmitToImageReference();
var mRef = CreateCompilationWithMscorlib("public interface M : B { }", new[] { bRef }, options: TestOptions.ReleaseModule.WithModuleName("M.netmodule")).EmitToImageReference();
var nRef = CreateCompilationWithMscorlib("public interface N : D { }", new[] { dRef }, options: TestOptions.ReleaseModule.WithModuleName("N.netmodule")).EmitToImageReference();
var aRef = CreateCompilationWithMscorlib("public interface A : D { }", new[] { dRef }, assemblyName: "A").EmitToImageReference();
var resolver = new TestMissingMetadataReferenceResolver(new Dictionary<string, MetadataReference>
{
{ "B", bRef },
{ "D", dRef },
});
var c = CreateCompilationWithMscorlib("public interface C : A { }", new[] { aRef, mRef, nRef },
TestOptions.ReleaseDll.WithMetadataReferenceResolver(resolver));
c.VerifyEmitDiagnostics();
Assert.Equal("B", ((AssemblySymbol)c.GetAssemblyOrModuleSymbol(bRef)).Name);
Assert.Equal("D", ((AssemblySymbol)c.GetAssemblyOrModuleSymbol(dRef)).Name);
}
[Fact]
public void MissingAssemblyResolution_BetterVersion_Higher_vs_Exact()
{
// c - a -> "b,v1"
// - "b,v3"
//
var b1Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference();
var b2Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference();
var b3Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""3.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference();
var aRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class A : B { }", new[] { b1Ref }, options: s_signedDll, assemblyName: "A").EmitToImageReference();
var c = CreateCompilationWithMscorlib("public class C : A { }", new[] { aRef, b3Ref },
s_signedDll.WithMetadataReferenceResolver(new TestMissingMetadataReferenceResolver(new Dictionary<string, MetadataReference>
{
// the compiler asked for v1, but we have v2
{ "B, 1.0.0.0", b2Ref }
})));
c.VerifyEmitDiagnostics(
// (1,18): warning CS1701: Assuming assembly reference
// 'B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' used by 'A' matches identity
// 'B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' of 'B', you may need to supply runtime policy
Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin, "A").WithArguments(
"B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "A",
"B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "B"));
Assert.Equal(
"B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2",
((AssemblySymbol)c.GetAssemblyOrModuleSymbol(b2Ref)).Identity.GetDisplayName());
}
[Fact]
public void MissingAssemblyResolution_BetterVersion_Higher_vs_BetterHigher()
{
// c - a -> "b,v1"
// - "b,v3"
//
var b1Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference();
var b2Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference();
var b3Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""3.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference();
var aRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class A : B { }", new[] { b1Ref }, options: s_signedDll, assemblyName: "A").EmitToImageReference();
var c = CreateCompilationWithMscorlib("public class C : A { }", new[] { aRef, b3Ref },
s_signedDll.WithMetadataReferenceResolver(new TestMissingMetadataReferenceResolver(new Dictionary<string, MetadataReference>
{
{ "B, 1.0.0.0", b2Ref }
})));
c.VerifyEmitDiagnostics(
// (1,18): warning CS1701: Assuming assembly reference
// 'B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' used by 'A' matches identity
// 'B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' of 'B', you may need to supply runtime policy
Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin, "A").WithArguments(
"B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "A",
"B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "B"));
}
[Fact]
public void MissingAssemblyResolution_BetterVersion_Higher_vs_WorseHigher()
{
// c - a -> "b,v1"
// - "b,v2"
//
var b1Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference();
var b2Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference();
var b3Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""3.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference();
var aRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class A : B { }", new[] { b1Ref }, options: s_signedDll, assemblyName: "A").EmitToImageReference();
var c = CreateCompilationWithMscorlib("public class C : A { }", new[] { aRef, b2Ref },
s_signedDll.WithMetadataReferenceResolver(new TestMissingMetadataReferenceResolver(new Dictionary<string, MetadataReference>
{
{ "B, 1.0.0.0", b3Ref }
})));
c.VerifyEmitDiagnostics(
// (1,18): warning CS1701: Assuming assembly reference
// 'B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' used by 'A' matches identity
// 'B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' of 'B', you may need to supply runtime policy
Diagnostic(ErrorCode.WRN_UnifyReferenceMajMin, "A").WithArguments(
"B, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "A",
"B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2", "B"));
}
[Fact]
public void MissingAssemblyResolution_BetterVersion_Lower_vs_Exact1()
{
// c - a -> "b,v2"
// - "b,v1"
//
var b1Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference();
var b2Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference();
var aRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class A : B { }", new[] { b2Ref }, options: s_signedDll, assemblyName: "A").EmitToImageReference();
var c = CreateCompilationWithMscorlib("public class C : A { }", new[] { aRef, b1Ref },
s_signedDll.WithMetadataReferenceResolver(new TestMissingMetadataReferenceResolver(new Dictionary<string, MetadataReference>
{
{ "B, 2.0.0.0", b2Ref }
})));
c.VerifyEmitDiagnostics();
Assert.Equal(
"B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2",
((AssemblySymbol)c.GetAssemblyOrModuleSymbol(b2Ref)).Identity.GetDisplayName());
}
[Fact]
public void MissingAssemblyResolution_BetterVersion_Lower_vs_WorseLower()
{
// c - a -> "b,v3"
// - "b,v2"
//
var b1Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference();
var b2Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference();
var b3Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""3.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference();
var aRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class A : B { }", new[] { b3Ref }, options: s_signedDll, assemblyName: "A").EmitToImageReference();
var c = CreateCompilationWithMscorlib("public class C : A { }", new[] { aRef, b2Ref },
s_signedDll.WithMetadataReferenceResolver(new TestMissingMetadataReferenceResolver(new Dictionary<string, MetadataReference>
{
{ "B, 3.0.0.0", b1Ref }
})));
c.VerifyDiagnostics(
// (1,18): error CS1705: Assembly
// 'A' with identity 'A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2'
// uses 'B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' which has a higher version than referenced assembly
// 'B' with identity 'B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2'
Diagnostic(ErrorCode.ERR_AssemblyMatchBadVersion, "A").WithArguments(
"A", "A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2",
"B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2",
"B", "B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2"));
}
[Fact]
public void MissingAssemblyResolution_BetterVersion_Lower_vs_BetterLower()
{
// c - a -> "b,v3"
// - "b,v1"
//
var b1Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference();
var b2Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""2.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference();
var b3Ref = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""3.0.0.0"")] public class B { }", options: s_signedDll, assemblyName: "B").EmitToImageReference();
var aRef = CreateCompilationWithMscorlib(@"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")] public class A : B { }", new[] { b3Ref }, options: s_signedDll, assemblyName: "A").EmitToImageReference();
var c = CreateCompilationWithMscorlib("public class C : A { }", new[] { aRef, b1Ref },
s_signedDll.WithMetadataReferenceResolver(new TestMissingMetadataReferenceResolver(new Dictionary<string, MetadataReference>
{
{ "B, 3.0.0.0", b2Ref }
})));
c.VerifyDiagnostics(
// (1,18): error CS1705: Assembly
// 'A' with identity 'A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2'
// uses 'B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2' which has a higher version than referenced assembly
// 'B' with identity 'B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2'
Diagnostic(ErrorCode.ERR_AssemblyMatchBadVersion, "A").WithArguments(
"A", "A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2",
"B, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2",
"B", "B, Version=2.0.0.0, Culture=neutral, PublicKeyToken=ce65828c82a341f2"));
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis
......@@ -12,5 +13,18 @@ public abstract class MetadataReferenceResolver
public abstract override bool Equals(object other);
public abstract override int GetHashCode();
public abstract ImmutableArray<PortableExecutableReference> ResolveReference(string reference, string baseFilePath, MetadataReferenceProperties properties);
/// <summary>
/// True to instruct the compiler to invoke <see cref="ResolveMissingAssembly(AssemblyIdentity)"/> for each assembly reference that
/// doesn't match any of the assemblies explicitly referenced by the <see cref="Compilation"/> (via <see cref="Compilation.ExternalReferences"/>, or #r directives.
/// </summary>
public virtual bool ResolveMissingAssemblies => false;
/// <summary>
/// Resolves a missing assembly reference.
/// </summary>
/// <param name="identity">Identity of the assembly reference.</param>
/// <returns>Resolved reference or null if the identity can't be resolved.</returns>
public virtual PortableExecutableReference ResolveMissingAssembly(AssemblyIdentity identity) => null;
}
}
......@@ -60,4 +60,6 @@ static Microsoft.CodeAnalysis.Diagnostics.Telemetry.AnalyzerTelemetry.GetAnalyze
static Microsoft.CodeAnalysis.Diagnostics.Telemetry.AnalyzerTelemetry.GetAnalyzerExecutionTime(this Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzers compilationWithAnalyzers, Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer analyzer) -> System.TimeSpan
static Microsoft.CodeAnalysis.SyntaxNodeExtensions.NormalizeWhitespace<TNode>(this TNode node, string indentation = " ", string eol = "\r\n", bool elasticTrivia = false) -> TNode
static Microsoft.CodeAnalysis.SyntaxNodeExtensions.NormalizeWhitespace<TNode>(this TNode node, string indentation, bool elasticTrivia) -> TNode
virtual Microsoft.CodeAnalysis.SourceReferenceResolver.ReadText(string resolvedPath) -> Microsoft.CodeAnalysis.Text.SourceText
virtual Microsoft.CodeAnalysis.MetadataReferenceResolver.ResolveMissingAssemblies.get -> bool
virtual Microsoft.CodeAnalysis.MetadataReferenceResolver.ResolveMissingAssembly(Microsoft.CodeAnalysis.AssemblyIdentity identity) -> Microsoft.CodeAnalysis.PortableExecutableReference
virtual Microsoft.CodeAnalysis.SourceReferenceResolver.ReadText(string resolvedPath) -> Microsoft.CodeAnalysis.Text.SourceText
\ No newline at end of file
......@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
namespace Microsoft.CodeAnalysis
{
......@@ -10,6 +11,7 @@ internal partial class CommonReferenceManager<TCompilation, TAssemblySymbol>
/// <summary>
/// Information about an assembly, used as an input for the Binder class.
/// </summary>
[DebuggerDisplay("{GetDebuggerDisplay(), nq}")]
internal abstract class AssemblyData
{
/// <summary>
......@@ -63,6 +65,8 @@ internal abstract class AssemblyData
/// Returns null otherwise.
/// </summary>
public abstract Compilation SourceCompilation { get; }
private string GetDebuggerDisplay() => $"{GetType().Name}: [{Identity.GetDisplayName()}]";
}
}
}
......@@ -20,10 +20,6 @@ protected sealed class AssemblyDataForAssemblyBeingBuilt : AssemblyData
// all referenced assembly names including assemblies referenced by modules:
private readonly ImmutableArray<AssemblyIdentity> _referencedAssemblies;
// [0] is the number of assembly names in referencedAssemblies that are direct references specified in referencedAssemblyData.
// [i] is the number of references coming from module moduleInfo[i-1]. These names are also added to referencedAssemblies.
private readonly int[] _moduleReferenceCounts;
public AssemblyDataForAssemblyBeingBuilt(
AssemblyIdentity identity,
ImmutableArray<AssemblyData> referencedAssemblyData,
......@@ -36,37 +32,21 @@ protected sealed class AssemblyDataForAssemblyBeingBuilt : AssemblyData
_referencedAssemblyData = referencedAssemblyData;
int count = modules.Length;
var refs = ArrayBuilder<AssemblyIdentity>.GetInstance(referencedAssemblyData.Length + count); //approximate size
var refs = ArrayBuilder<AssemblyIdentity>.GetInstance(referencedAssemblyData.Length + modules.Length); //approximate size
foreach (AssemblyData data in referencedAssemblyData)
{
refs.Add(data.Identity);
}
_moduleReferenceCounts = new int[count + 1]; // Plus one for the source module.
_moduleReferenceCounts[0] = refs.Count;
// add assembly names from modules:
for (int i = 1; i <= count; i++)
for (int i = 1; i <= modules.Length; i++)
{
var module = modules[i - 1];
_moduleReferenceCounts[i] = module.ReferencedAssemblies.Length;
refs.AddRange(module.ReferencedAssemblies);
refs.AddRange(modules[i - 1].ReferencedAssemblies);
}
_referencedAssemblies = refs.ToImmutableAndFree();
}
public int[] ReferencesCountForModule
{
get
{
return _moduleReferenceCounts;
}
}
public override AssemblyIdentity Identity
{
get
......@@ -103,14 +83,17 @@ public override IEnumerable<TAssemblySymbol> AvailableSymbols
boundReferences[i] = new AssemblyReferenceBinding(assemblies[i + 1].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,
assemblyIdentityComparer,
okToResolveAgainstCompilationBeingCreated: false); // references from added modules shouldn't resolve against the assembly we are building.
definitionStartIndex,
assemblyIdentityComparer);
}
return boundReferences;
......
......@@ -95,7 +95,6 @@ internal AssemblyIdentity ReferenceIdentity
{
get
{
Debug.Assert(IsBound);
return _referenceIdentity;
}
}
......
......@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.Collections;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis
......@@ -17,14 +18,25 @@ internal partial class CommonReferenceManager<TCompilation, TAssemblySymbol>
///
/// The first element (index==0) of the assemblies array represents the assembly being built.
/// One can think about the rest of the items in assemblies array as assembly references given to the compiler to
///
/// build executable for the assembly being built.
///
/// </summary>
///
/// <param name="assemblies">
/// The set of <see cref="AssemblyData"/> objects describing assemblies, for which this method should
/// Input: An array of <see cref="AssemblyData"/> objects describing assemblies, for which this method should
/// resolve references and find suitable AssemblySymbols.
/// Output: Updated array with resolved implicitly referenced assemblies appended.
/// </param>
/// <param name="resolverOpt">
/// Reference resolver used to look up missing assemblies.
/// </param>
/// <param name="importOptions">
/// Import options applied to implicitly resolved references.
/// </param>
/// <param name="implicitlyResolvedReferences">
/// Implicitly resolved references, corresponding to <see cref="AssemblyData"/> appended to the <paramref name="assemblies"/> array.
/// </param>
/// <param name="resolutionDiagnostics">
/// Any diagnostics reported while resolving missing assemblies.
/// </param>
/// <param name="hasCircularReference">
/// True if the assembly being compiled is indirectly referenced through some of its own references.
......@@ -32,7 +44,7 @@ internal partial class CommonReferenceManager<TCompilation, TAssemblySymbol>
/// <param name="corLibraryIndex">
/// The definition index of the COR library.
/// </param>
/// <returns>
/// <return>
/// An array of <see cref="BoundInputAssembly"/> structures describing the result. It has the same amount of items as
/// the input assemblies array, <see cref="BoundInputAssembly"/> for each input AssemblyData object resides
/// at the same position.
......@@ -45,60 +57,224 @@ 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.
/// </returns>
/// </return>
internal BoundInputAssembly[] Bind(
ImmutableArray<AssemblyData> assemblies,
ref ImmutableArray<AssemblyData> assemblies,
MetadataReferenceResolver resolverOpt,
MetadataImportOptions importOptions,
ArrayBuilder<MetadataReference> implicitlyResolvedReferences,
DiagnosticBag resolutionDiagnostics,
out bool hasCircularReference,
out int corLibraryIndex)
{
Debug.Assert(!assemblies.IsDefault);
int totalAssemblies = assemblies.Length;
var referenceBindings = ArrayBuilder<AssemblyReferenceBinding[]>.GetInstance();
try
{
// Based on assembly identity, for each assembly,
// bind its references against the other assemblies we have.
for (int i = 0; i < assemblies.Length; i++)
{
referenceBindings.Add(assemblies[i].BindAssemblyReferences(assemblies, IdentityComparer));
}
if (resolverOpt?.ResolveMissingAssemblies == true)
{
ResolveAndBindMissingAssemblies(assemblies, referenceBindings, implicitlyResolvedReferences, resolverOpt, importOptions, resolutionDiagnostics, out assemblies);
}
hasCircularReference = CheckCircularReference(referenceBindings);
corLibraryIndex = IndexOfCorLibrary(assemblies);
// For each assembly, locate AssemblySymbol with similar reference resolution
// What does similar mean?
// Similar means:
// 1) The same references are resolved against the assemblies that we are given.
// 2) The same assembly is used as the COR library.
var boundInputs = new BoundInputAssembly[referenceBindings.Count];
for (int i = 0; i < referenceBindings.Count; i++)
{
boundInputs[i].ReferenceBinding = referenceBindings[i];
}
var candidateInputAssemblySymbols = new TAssemblySymbol[assemblies.Length];
// If any assembly from assemblies array refers back to assemblyBeingBuilt,
// we know that we cannot reuse symbols for any assemblies containing NoPia
// local types. Because we cannot reuse symbols for assembly referring back
// to assemblyBeingBuilt.
if (!hasCircularReference)
{
// Deal with assemblies containing NoPia local types.
if (ReuseAssemblySymbolsWithNoPiaLocalTypes(boundInputs, candidateInputAssemblySymbols, assemblies, corLibraryIndex))
{
return boundInputs;
}
}
// This is the array where we store the result in.
BoundInputAssembly[] boundInputs = new BoundInputAssembly[totalAssemblies];
// NoPia shortcut either didn't apply or failed, go through general process
// of matching candidates.
// Based on assembly identity, for each assembly,
// bind its references against the other assemblies we have.
for (int i = 0; i < totalAssemblies; i++)
ReuseAssemblySymbols(boundInputs, candidateInputAssemblySymbols, assemblies, corLibraryIndex);
return boundInputs;
}
finally
{
boundInputs[i].ReferenceBinding = assemblies[i].BindAssemblyReferences(assemblies, this.IdentityComparer);
referenceBindings.Free();
}
}
// All assembly symbols should be uninitialized at this point:
Debug.Assert(Array.TrueForAll(boundInputs, bi => bi.AssemblySymbol == null));
private void ResolveAndBindMissingAssemblies(
ImmutableArray<AssemblyData> explicitAssemblies,
ArrayBuilder<AssemblyReferenceBinding[]> referenceBindings,
ArrayBuilder<MetadataReference> resolvedReferences,
MetadataReferenceResolver resolver,
MetadataImportOptions importOptions,
DiagnosticBag resolutionDiagnostics,
out ImmutableArray<AssemblyData> allAssemblies)
{
var implicitAssemblies = ArrayBuilder<AssemblyData>.GetInstance();
hasCircularReference = CheckCircularReference(boundInputs);
// tracks identities we already asked the resolver to resolve:
var resolvedMissingIdentities = PooledHashSet<AssemblyIdentity>.GetInstance();
corLibraryIndex = IndexOfCorLibrary(assemblies);
// reference bindings of implicit assemblies, use to calculate a fixed point:
var referenceBindingsToProcess = ArrayBuilder<AssemblyReferenceBinding[]>.GetInstance();
// For each assembly, locate AssemblySymbol with similar reference resolution
// What does similar mean?
// Similar means:
// 1) The same references are resolved against the assemblies that we are given.
// 2) The same assembly is used as the COR library.
try
{
// collect all missing identities, resolve the assemblies and bind their references against explicit definitions:
referenceBindingsToProcess.AddRange(referenceBindings);
TAssemblySymbol[] candidateInputAssemblySymbols = new TAssemblySymbol[totalAssemblies];
while (referenceBindingsToProcess.Count > 0)
{
foreach (var binding in referenceBindingsToProcess.Pop())
{
// try resolve a reference if eitehr the binding failed or didn't find an exact match:
if (binding.IsBound && binding.VersionDifference == 0 ||
resolvedMissingIdentities.Contains(binding.ReferenceIdentity))
{
continue;
}
// If any assembly from assemblies array refers back to assemblyBeingBuilt,
// we know that we cannot reuse symbols for any assemblies containing NoPia
// local types. Because we cannot reuse symbols for assembly referring back
// to assemblyBeingBuilt.
if (!hasCircularReference)
{
// Deal with assemblies containing NoPia local types.
if (ReuseAssemblySymbolsWithNoPiaLocalTypes(boundInputs, candidateInputAssemblySymbols, assemblies, corLibraryIndex))
var peReference = resolver.ResolveMissingAssembly(binding.ReferenceIdentity);
if (peReference == null)
{
continue;
}
var data = ResolveMissingAssembly(binding.ReferenceIdentity, peReference, importOptions, resolutionDiagnostics);
if (data == null)
{
continue;
}
resolvedReferences.Add(peReference);
resolvedMissingIdentities.Add(binding.ReferenceIdentity);
implicitAssemblies.Add(data);
var referenceBinding = data.BindAssemblyReferences(explicitAssemblies, IdentityComparer);
referenceBindings.Add(referenceBinding);
referenceBindingsToProcess.Push(referenceBinding);
}
}
// NB: includes the assembly being built:
int explicitAssemblyCount = explicitAssemblies.Length;
allAssemblies = explicitAssemblies.AddRange(implicitAssemblies);
// Rebind assembly referenced that were initially missing or were bound to a definitions with a different version
// (with more assembly identities available we may have a better match now).
if (implicitAssemblies.Count > 0)
{
return boundInputs;
foreach (var referenceBinding in referenceBindings)
{
for (int i = 0; i < referenceBinding.Length; i++)
{
var binding = referenceBinding[i];
if (!binding.IsBound || binding.VersionDifference != 0)
{
// We only need to resolve against implicitly resolved assemblies,
// since we already resolved against explicitly specified ones.
var newBinding = ResolveReferencedAssembly(
binding.ReferenceIdentity,
allAssemblies,
explicitAssemblyCount,
IdentityComparer);
if (IsBetterVersionMatch(binding.ReferenceIdentity, allAssemblies, binding, newBinding))
{
referenceBinding[i] = newBinding;
}
}
}
}
UpdateBindingsOfAssemblyBeingBuilt(referenceBindings, explicitAssemblies, implicitAssemblies);
}
}
finally
{
implicitAssemblies.Free();
resolvedMissingIdentities.Free();
referenceBindingsToProcess.Free();
}
}
private static void UpdateBindingsOfAssemblyBeingBuilt(ArrayBuilder<AssemblyReferenceBinding[]> referenceBindings, ImmutableArray<AssemblyData> explicitAssemblies, ArrayBuilder<AssemblyData> implicitAssemblies)
{
// add implicitly resolved assemblies to the bindings of the assembly being built:
var bindingsOfAssemblyBeingBuilt = ArrayBuilder<AssemblyReferenceBinding>.GetInstance(referenceBindings[0].Length + implicitAssemblies.Count);
// add bindings for explicitly specified assemblies (-1 for the assembly being built):
bindingsOfAssemblyBeingBuilt.AddRange(referenceBindings[0], explicitAssemblies.Length - 1);
// add bindings for implicitly resolved assemblies:
for (int i = 0; i < implicitAssemblies.Count; i++)
{
bindingsOfAssemblyBeingBuilt.Add(new AssemblyReferenceBinding(implicitAssemblies[i].Identity, explicitAssemblies.Length + i));
}
// NoPia shortcut either didn't apply or failed, go through general process
// of matching candidates.
// add bindings for assemblies referenced by modules added to the compilation:
bindingsOfAssemblyBeingBuilt.AddRange(referenceBindings[0], explicitAssemblies.Length - 1, referenceBindings[0].Length - explicitAssemblies.Length + 1);
ReuseAssemblySymbols(boundInputs, candidateInputAssemblySymbols, assemblies, corLibraryIndex);
referenceBindings[0] = bindingsOfAssemblyBeingBuilt.ToArrayAndFree();
}
internal AssemblyData ResolveMissingAssembly(
AssemblyIdentity identity,
PortableExecutableReference peReference,
MetadataImportOptions importOptions,
DiagnosticBag diagnostics)
{
var metadata = GetMetadata(peReference, MessageProvider, Location.None, diagnostics);
Debug.Assert(metadata != null || diagnostics.HasAnyErrors());
if (metadata == null)
{
return null;
}
var assemblyMetadata = metadata as AssemblyMetadata;
if (assemblyMetadata?.IsValidAssembly() != true)
{
diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_MetadataFileNotAssembly, Location.None, peReference.Display));
return null;
}
return boundInputs;
return CreateAssemblyDataForFile(
assemblyMetadata.GetAssembly(),
assemblyMetadata.CachedSymbols,
peReference.DocumentationProvider,
SimpleAssemblyName,
importOptions,
peReference.Properties.EmbedInteropTypes);
}
private bool ReuseAssemblySymbolsWithNoPiaLocalTypes(BoundInputAssembly[] boundInputs, TAssemblySymbol[] candidateInputAssemblySymbols, ImmutableArray<AssemblyData> assemblies, int corLibraryIndex)
......@@ -458,11 +634,11 @@ private void ReuseAssemblySymbols(BoundInputAssembly[] boundInputs, TAssemblySym
}
}
private static bool CheckCircularReference(BoundInputAssembly[] boundInputs)
private static bool CheckCircularReference(IReadOnlyList<AssemblyReferenceBinding[]> referenceBindings)
{
for (int i = 1; i < boundInputs.Length; i++)
for (int i = 1; i < referenceBindings.Count; i++)
{
foreach (AssemblyReferenceBinding index in boundInputs[i].ReferenceBinding)
foreach (AssemblyReferenceBinding index in referenceBindings[i])
{
if (index.BoundToAssemblyBeingBuilt)
{
......
......@@ -767,13 +767,13 @@ private static PortableExecutableReference ResolveReferenceDirective(string refe
internal static AssemblyReferenceBinding[] ResolveReferencedAssemblies(
ImmutableArray<AssemblyIdentity> references,
ImmutableArray<AssemblyData> definitions,
AssemblyIdentityComparer assemblyIdentityComparer,
bool okToResolveAgainstCompilationBeingCreated)
int definitionStartIndex,
AssemblyIdentityComparer assemblyIdentityComparer)
{
var boundReferences = new AssemblyReferenceBinding[references.Length];
for (int j = 0; j < references.Length; j++)
{
boundReferences[j] = ResolveReferencedAssembly(references[j], definitions, assemblyIdentityComparer, okToResolveAgainstCompilationBeingCreated);
boundReferences[j] = ResolveReferencedAssembly(references[j], definitions, definitionStartIndex, assemblyIdentityComparer);
}
return boundReferences;
......@@ -783,17 +783,17 @@ private static PortableExecutableReference ResolveReferenceDirective(string refe
/// Used to match AssemblyRef with AssemblyDef.
/// </summary>
/// <param name="definitions">Array of definition identities to match against.</param>
/// <param name="definitionStartIndex">An index of the first definition to consider, <paramref name="definitions"/> preceding this index are ignored.</param>
/// <param name="reference">Reference identity to resolve.</param>
/// <param name="assemblyIdentityComparer">Assembly identity comparer.</param>
/// <param name="okToResolveAgainstCompilationBeingCreated"> Is it Ok to resolve reference against the compilation we are creating?</param>
/// <returns>
/// Returns an index the reference is bound.
/// </returns>
internal static AssemblyReferenceBinding ResolveReferencedAssembly(
AssemblyIdentity reference,
ImmutableArray<AssemblyData> definitions,
AssemblyIdentityComparer assemblyIdentityComparer,
bool okToResolveAgainstCompilationBeingCreated)
int definitionStartIndex,
AssemblyIdentityComparer assemblyIdentityComparer)
{
// Dev11 C# compiler allows the versions to not match exactly, assuming that a newer library may be used instead of an older version.
// For a given reference it finds a definition with the lowest version that is higher then or equal to the reference version.
......@@ -803,9 +803,11 @@ private static PortableExecutableReference ResolveReferenceDirective(string refe
int minHigherVersionDefinition = -1;
int maxLowerVersionDefinition = -1;
// NB: Start at 1, since we checked 0 above.
const int definitionOffset = 1;
for (int i = definitionOffset; i < definitions.Length; i++)
// Skip assembly being built for now; it will be considered at the very end:
bool resolveAgainstAssemblyBeingBuilt = definitionStartIndex == 0;
definitionStartIndex = Math.Max(definitionStartIndex, 1);
for (int i = definitionStartIndex; i < definitions.Length; i++)
{
AssemblyIdentity definition = definitions[i].Identity;
......@@ -860,7 +862,7 @@ private static PortableExecutableReference ResolveReferenceDirective(string refe
// substitute for a collection of Windows.*.winmd compile-time references.
if (reference.IsWindowsComponent())
{
for (int i = definitionOffset; i < definitions.Length; i++)
for (int i = definitionStartIndex; i < definitions.Length; i++)
{
if (definitions[i].Identity.IsWindowsRuntime())
{
......@@ -878,7 +880,7 @@ private static PortableExecutableReference ResolveReferenceDirective(string refe
// allow the compilation to match the reference.
if (reference.ContentType == AssemblyContentType.WindowsRuntime)
{
for (int i = definitionOffset; i < definitions.Length; i++)
for (int i = definitionStartIndex; i < definitions.Length; i++)
{
var definition = definitions[i].Identity;
var sourceCompilation = definitions[i].SourceCompilation;
......@@ -898,7 +900,7 @@ private static PortableExecutableReference ResolveReferenceDirective(string refe
// As in the native compiler (see IMPORTER::MapAssemblyRefToAid), we compare against the
// compilation (i.e. source) assembly as a last resort. We follow the native approach of
// skipping the public key comparison since we have yet to compute it.
if (okToResolveAgainstCompilationBeingCreated &&
if (resolveAgainstAssemblyBeingBuilt &&
AssemblyIdentityComparer.SimpleNameComparer.Equals(reference.Name, definitions[0].Identity.Name))
{
Debug.Assert(definitions[0].Identity.PublicKeyToken.IsEmpty);
......@@ -907,5 +909,28 @@ private static PortableExecutableReference ResolveReferenceDirective(string refe
return new AssemblyReferenceBinding(reference);
}
private static bool IsBetterVersionMatch(AssemblyIdentity reference, ImmutableArray<AssemblyData> definitions, AssemblyReferenceBinding previousBinding, AssemblyReferenceBinding newBinding)
{
Debug.Assert(newBinding.IsBound);
// Previous binding failed or new binding is an exact match
if (!previousBinding.IsBound || newBinding.VersionDifference == 0)
{
return true;
}
if (previousBinding.VersionDifference != newBinding.VersionDifference)
{
// lesser than reference version is worst than higher than reference version
return previousBinding.VersionDifference < 0;
}
var previousVersion = definitions[previousBinding.DefinitionIndex].Identity.Version;
var newVersion = definitions[newBinding.DefinitionIndex].Identity.Version;
// closer version is better:
return (newBinding.VersionDifference > 0) ? newVersion < previousVersion : newVersion > previousVersion;
}
}
}
......@@ -323,8 +323,6 @@ internal void AssertBound()
// lazyCorLibrary is null if the compilation is corlib
Debug.Assert(_lazyReferencedAssemblies.Length == 0 || _lazyCorLibraryOpt != null);
Debug.Assert(_lazyReferencedAssemblies.Length == _lazyAliasesOfReferencedAssemblies.Length);
}
[Conditional("DEBUG")]
......@@ -362,6 +360,7 @@ internal bool IsBound
Debug.Assert(referencedModules.Length == referencedModulesReferences.Length);
Debug.Assert(referencedModules.Length == referencedModulesMap.Count);
Debug.Assert(referencedAssemblies.Length == aliasesOfReferencedAssemblies.Length);
_lazyReferencedAssembliesMap = referencedAssembliesMap;
_lazyReferencedModuleIndexMap = referencedModulesMap;
......@@ -381,6 +380,63 @@ internal bool IsBound
Interlocked.Exchange(ref _isBound, 1);
}
protected static void BuildReferencedAssembliesAndModulesMaps(
ImmutableArray<MetadataReference> references,
ImmutableArray<ResolvedReference> referenceMap,
IReadOnlyList<MetadataReference> implicitlyResolvedReferences,
int referencedAssemblyCount,
int referencedModuleCount,
out Dictionary<MetadataReference, int> referencedAssembliesMap,
out Dictionary<MetadataReference, int> referencedModulesMap,
out ImmutableArray<ImmutableArray<string>> aliasesOfReferencedAssemblies)
{
referencedAssembliesMap = new Dictionary<MetadataReference, int>(referenceMap.Length + implicitlyResolvedReferences.Count);
referencedModulesMap = new Dictionary<MetadataReference, int>(referencedModuleCount);
var aliasesOfReferencedAssembliesBuilder = ArrayBuilder<ImmutableArray<string>>.GetInstance(referencedAssemblyCount);
for (int i = 0; i < referenceMap.Length; i++)
{
if (referenceMap[i].IsSkipped)
{
continue;
}
if (referenceMap[i].Kind == MetadataImageKind.Module)
{
// add 1 for the manifest module:
int moduleIndex = 1 + referenceMap[i].Index;
referencedModulesMap.Add(references[i], moduleIndex);
}
else
{
// index into assembly data array
int assemblyIndex = referenceMap[i].Index;
Debug.Assert(aliasesOfReferencedAssembliesBuilder.Count == assemblyIndex);
referencedAssembliesMap.Add(references[i], assemblyIndex);
aliasesOfReferencedAssembliesBuilder.Add(referenceMap[i].Aliases);
}
}
for (int i = 0; i < implicitlyResolvedReferences.Count; i++)
{
var assemblyIndex = referencedAssemblyCount + i;
Debug.Assert(aliasesOfReferencedAssembliesBuilder.Count == assemblyIndex);
referencedAssembliesMap.Add(implicitlyResolvedReferences[i], assemblyIndex);
// Use aliases specified on the reference returned by the missing assembly resolver.
// Unlike explicitly given references, we don't apply de-duplication logic on references
// returned by the resolver. This is because we already have an assembly identity
// (rather than an opaque reference string or a preexisting metadata reference)
// and we don't ask the resolver to resolve an identity that matches identities
// we have already resolved. Hence there are no opportunities for reference duplication.
aliasesOfReferencedAssembliesBuilder.Add(implicitlyResolvedReferences[i].Properties.Aliases);
}
aliasesOfReferencedAssemblies = aliasesOfReferencedAssembliesBuilder.ToImmutableAndFree();
}
#region Compilation APIs Implementation
// for testing purposes
......
......@@ -383,6 +383,14 @@ public void AddRange(ImmutableArray<T> items, int length)
_builder.AddRange(items, length);
}
public void AddRange(T[] items, int start, int length)
{
for (int i = 0; i < length; i++)
{
Add(items[start + i]);
}
}
public void AddRange(IEnumerable<T> items)
{
_builder.AddRange(items);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册