| File: Symbols\Compilation_UsedAssemblies.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.Threading; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { public partial class CSharpCompilation { private ConcurrentSet<AssemblySymbol>? _lazyUsedAssemblyReferences; private bool _usedAssemblyReferencesFrozen; public override ImmutableArray<MetadataReference> GetUsedAssemblyReferences(CancellationToken cancellationToken = default) { ConcurrentSet<AssemblySymbol>? usedAssemblies = GetCompleteSetOfUsedAssemblies(cancellationToken); if (usedAssemblies is null) { return ImmutableArray<MetadataReference>.Empty; } var setOfReferences = new HashSet<MetadataReference>(ReferenceEqualityComparer.Instance); ImmutableDictionary<MetadataReference, ImmutableArray<MetadataReference>> mergedAssemblyReferencesMap = GetBoundReferenceManager().MergedAssemblyReferencesMap; foreach (var reference in References) { if (reference.Properties.Kind == MetadataImageKind.Assembly) { Symbol? symbol = GetBoundReferenceManager().GetReferencedAssemblySymbol(reference); if (symbol is object && usedAssemblies.Contains((AssemblySymbol)symbol) && setOfReferences.Add(reference) && mergedAssemblyReferencesMap.TryGetValue(reference, out ImmutableArray<MetadataReference> merged)) { // Include all "merged" references as well because they might "define" used extern aliases. setOfReferences.AddAll(merged); } } } // Use stable ordering for the result, matching the order in References. var builder = ArrayBuilder<MetadataReference>.GetInstance(setOfReferences.Count); foreach (var reference in References) { if (setOfReferences.Contains(reference)) { builder.Add(reference); } } return builder.ToImmutableAndFree(); } private ConcurrentSet<AssemblySymbol>? GetCompleteSetOfUsedAssemblies(CancellationToken cancellationToken) { if (!_usedAssemblyReferencesFrozen && !Volatile.Read(ref _usedAssemblyReferencesFrozen)) { var diagnostics = BindingDiagnosticBag.GetConcurrentInstance(); RoslynDebug.Assert(diagnostics.DiagnosticBag is object); GetDiagnosticsWithoutSeverityFiltering(CompilationStage.Declare, includeEarlierStages: true, diagnostics, symbolFilter: null, cancellationToken); bool seenErrors = diagnostics.HasAnyErrors(); if (!seenErrors) { diagnostics.DiagnosticBag.Clear(); GetDiagnosticsForAllMethodBodies(diagnostics, doLowering: true, cancellationToken); seenErrors = diagnostics.HasAnyErrors(); if (!seenErrors) { AddUsedAssemblies(diagnostics.DependenciesBag); } } completeTheSetOfUsedAssemblies(seenErrors, cancellationToken); diagnostics.Free(); } return _lazyUsedAssemblyReferences; void addUsedAssembly(AssemblySymbol dependency, ArrayBuilder<AssemblySymbol> stack) { if (AddUsedAssembly(dependency)) { stack.Push(dependency); } } void addReferencedAssemblies(AssemblySymbol assembly, bool includeMainModule, ArrayBuilder<AssemblySymbol> stack) { for (int i = (includeMainModule ? 0 : 1); i < assembly.Modules.Length; i++) { foreach (var dependency in assembly.Modules[i].ReferencedAssemblySymbols) { addUsedAssembly(dependency, stack); } } } void completeTheSetOfUsedAssemblies(bool seenErrors, CancellationToken cancellationToken) { if (_usedAssemblyReferencesFrozen || Volatile.Read(ref _usedAssemblyReferencesFrozen)) { return; } if (seenErrors) { // Add all referenced assemblies foreach (var assembly in SourceModule.ReferencedAssemblySymbols) { AddUsedAssembly(assembly); } } else { // Assume that all assemblies used by the added modules are also used for (int i = 1; i < SourceAssembly.Modules.Length; i++) { foreach (var dependency in SourceAssembly.Modules[i].ReferencedAssemblySymbols) { AddUsedAssembly(dependency); } } if (_usedAssemblyReferencesFrozen || Volatile.Read(ref _usedAssemblyReferencesFrozen)) { return; } // Assume that all assemblies used by the used assemblies are also used // This, for example, takes care of including facade assemblies that forward types around. if (_lazyUsedAssemblyReferences is object) { lock (_lazyUsedAssemblyReferences) { if (_usedAssemblyReferencesFrozen || Volatile.Read(ref _usedAssemblyReferencesFrozen)) { return; } var stack = ArrayBuilder<AssemblySymbol>.GetInstance(_lazyUsedAssemblyReferences.Count); stack.AddRange(_lazyUsedAssemblyReferences); while (stack.Count != 0) { AssemblySymbol current = stack.Pop(); ConcurrentSet<AssemblySymbol>? usedAssemblies; switch (current) { case SourceAssemblySymbol sourceAssembly: // The set of assemblies used by the referenced compilation feels like // a reasonable approximation to the set of assembly references that would // be emitted into the resulting binary for that compilation. An alternative // would be to attempt to emit and get the exact set of emitted references // in case of success. This might be too slow though. usedAssemblies = sourceAssembly.DeclaringCompilation.GetCompleteSetOfUsedAssemblies(cancellationToken); if (usedAssemblies is object) { foreach (AssemblySymbol dependency in usedAssemblies) { Debug.Assert(!dependency.IsLinked); addUsedAssembly(dependency, stack); } } break; case RetargetingAssemblySymbol retargetingAssembly: usedAssemblies = retargetingAssembly.UnderlyingAssembly.DeclaringCompilation.GetCompleteSetOfUsedAssemblies(cancellationToken); if (usedAssemblies is object) { foreach (AssemblySymbol underlyingDependency in retargetingAssembly.UnderlyingAssembly.SourceModule.ReferencedAssemblySymbols) { if (!underlyingDependency.IsLinked && usedAssemblies.Contains(underlyingDependency)) { AssemblySymbol dependency; if (!((RetargetingModuleSymbol)retargetingAssembly.Modules[0]).RetargetingDefinitions(underlyingDependency, out dependency)) { Debug.Assert(retargetingAssembly.Modules[0].ReferencedAssemblySymbols.Contains(underlyingDependency)); dependency = underlyingDependency; } addUsedAssembly(dependency, stack); } } } addReferencedAssemblies(retargetingAssembly, includeMainModule: false, stack); break; default: addReferencedAssemblies(current, includeMainModule: true, stack); break; } } stack.Free(); } } if (SourceAssembly.CorLibrary is object) { // Add core library AddUsedAssembly(SourceAssembly.CorLibrary); } } _usedAssemblyReferencesFrozen = true; } } internal void AddUsedAssemblies(ICollection<AssemblySymbol>? assemblies) { if (!assemblies.IsNullOrEmpty()) { foreach (var candidate in assemblies) { AddUsedAssembly(candidate); } } } internal bool AddUsedAssembly(AssemblySymbol? assembly) { if (assembly is null || assembly == SourceAssembly || assembly.IsMissing) { return false; } if (_lazyUsedAssemblyReferences is null) { Interlocked.CompareExchange(ref _lazyUsedAssemblyReferences, new ConcurrentSet<AssemblySymbol>(), null); } #if DEBUG bool wasFrozen = _usedAssemblyReferencesFrozen; #endif bool added = _lazyUsedAssemblyReferences.Add(assembly); #if DEBUG Debug.Assert(!added || !wasFrozen); #endif return added; } } }