File: Symbols\UsedAssemblies.vb
Web Access
Project: src\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' 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.
 
Imports System.Collections.Immutable
Imports System.Threading
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Retargeting
 
Namespace Microsoft.CodeAnalysis.VisualBasic
 
    Partial Public Class VisualBasicCompilation
 
        Private _lazyUsedAssemblyReferences As ConcurrentSet(Of AssemblySymbol)
        Private _usedAssemblyReferencesFrozen As Boolean
 
        Public Overrides Function GetUsedAssemblyReferences(Optional cancellationToken As CancellationToken = Nothing) As ImmutableArray(Of MetadataReference)
            Dim usedAssemblies As ConcurrentSet(Of AssemblySymbol) = GetCompleteSetOfUsedAssemblies(cancellationToken)
 
            If usedAssemblies Is Nothing Then
                Return ImmutableArray(Of MetadataReference).Empty
            End If
 
            ' Use stable ordering for the result, matching the order in References.
            Dim builder = ArrayBuilder(Of MetadataReference).GetInstance(usedAssemblies.Count)
 
            For Each reference In References
                If reference.Properties.Kind = MetadataImageKind.Assembly Then
                    Dim symbol As Symbol = GetBoundReferenceManager().GetReferencedAssemblySymbol(reference)
 
                    If symbol IsNot Nothing AndAlso usedAssemblies.Contains(DirectCast(symbol, AssemblySymbol)) Then
                        builder.Add(reference)
                    End If
                End If
            Next
 
            Return builder.ToImmutableAndFree()
        End Function
 
        Private Function GetCompleteSetOfUsedAssemblies(cancellationToken As CancellationToken) As ConcurrentSet(Of AssemblySymbol)
 
            If Not _usedAssemblyReferencesFrozen AndAlso Not Volatile.Read(_usedAssemblyReferencesFrozen) Then
 
                Dim diagnostics = BindingDiagnosticBag.GetConcurrentInstance()
                RoslynDebug.Assert(diagnostics.AccumulatesDiagnostics)
 
                GetDiagnosticsWithoutFiltering(CompilationStage.Declare, includeEarlierStages:=True, diagnostics, cancellationToken)
 
                Dim seenErrors As Boolean = diagnostics.HasAnyErrors()
 
                If Not seenErrors Then
                    diagnostics.DiagnosticBag.Clear()
                    GetDiagnosticsForAllMethodBodies(hasDeclarationErrors:=False, diagnostics, doLowering:=True, cancellationToken)
                    seenErrors = diagnostics.HasAnyErrors()
 
                    If Not seenErrors Then
                        AddUsedAssemblies(diagnostics.DependenciesBag)
                    End If
                End If
 
                CompleteTheSetOfUsedAssemblies(seenErrors, cancellationToken)
 
                diagnostics.Free()
            End If
 
            Return _lazyUsedAssemblyReferences
        End Function
 
        Private Sub AddUsedAssembly(dependency As AssemblySymbol, stack As ArrayBuilder(Of AssemblySymbol))
            If AddUsedAssembly(dependency) Then
                stack.Push(dependency)
            End If
        End Sub
 
        Private Sub AddReferencedAssemblies(assembly As AssemblySymbol, includeMainModule As Boolean, stack As ArrayBuilder(Of AssemblySymbol))
            For i As Integer = If(includeMainModule, 0, 1) To assembly.Modules.Length - 1
                For Each dependency In assembly.Modules(i).ReferencedAssemblySymbols
                    AddUsedAssembly(dependency, stack)
                Next
            Next
        End Sub
 
        Private Sub CompleteTheSetOfUsedAssemblies(seenErrors As Boolean, cancellationToken As CancellationToken)
 
            If _usedAssemblyReferencesFrozen OrElse Volatile.Read(_usedAssemblyReferencesFrozen) Then
                Return
            End If
 
            If seenErrors Then
                ' Add all referenced assemblies
                For Each assembly As AssemblySymbol In SourceModule.ReferencedAssemblySymbols
                    AddUsedAssembly(assembly)
                Next
            Else
                ' Assume that all assemblies used by the added modules are also used
                For i As Integer = 1 To SourceAssembly.Modules.Length - 1
                    For Each dependency In SourceAssembly.Modules(i).ReferencedAssemblySymbols
                        AddUsedAssembly(dependency)
                    Next
                Next
 
                If _usedAssemblyReferencesFrozen OrElse Volatile.Read(_usedAssemblyReferencesFrozen) Then
                    Return
                End If
 
                ' 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 IsNot Nothing Then
                    SyncLock _lazyUsedAssemblyReferences
                        If _usedAssemblyReferencesFrozen OrElse Volatile.Read(_usedAssemblyReferencesFrozen) Then
                            Return
                        End If
 
                        Dim stack = ArrayBuilder(Of AssemblySymbol).GetInstance(_lazyUsedAssemblyReferences.Count)
                        stack.AddRange(_lazyUsedAssemblyReferences)
 
                        While stack.Count <> 0
                            Dim current As AssemblySymbol = stack.Pop()
                            Dim usedAssemblies As ConcurrentSet(Of AssemblySymbol)
 
                            Dim sourceAssembly = TryCast(current, SourceAssemblySymbol)
 
                            If sourceAssembly IsNot Nothing Then
                                ' 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 IsNot Nothing Then
                                    For Each dependency As AssemblySymbol In usedAssemblies
                                        Debug.Assert(Not dependency.IsLinked)
                                        AddUsedAssembly(dependency, stack)
                                    Next
                                End If
 
                                Continue While
                            End If
 
                            Dim retargetingAssembly = TryCast(current, RetargetingAssemblySymbol)
 
                            If retargetingAssembly IsNot Nothing Then
                                usedAssemblies = retargetingAssembly.UnderlyingAssembly.DeclaringCompilation.GetCompleteSetOfUsedAssemblies(cancellationToken)
                                If usedAssemblies IsNot Nothing Then
                                    For Each underlyingDependency As AssemblySymbol In retargetingAssembly.UnderlyingAssembly.SourceModule.ReferencedAssemblySymbols
                                        If Not underlyingDependency.IsLinked AndAlso usedAssemblies.Contains(underlyingDependency) Then
                                            Dim dependency As AssemblySymbol = Nothing
 
                                            If Not DirectCast(retargetingAssembly.Modules(0), RetargetingModuleSymbol).RetargetingDefinitions(underlyingDependency, dependency) Then
                                                Debug.Assert(retargetingAssembly.Modules(0).ReferencedAssemblySymbols.Contains(underlyingDependency))
                                                dependency = underlyingDependency
                                            End If
 
                                            AddUsedAssembly(dependency, stack)
                                        End If
                                    Next
                                End If
 
                                AddReferencedAssemblies(retargetingAssembly, includeMainModule:=False, stack)
 
                                Continue While
                            End If
 
                            AddReferencedAssemblies(current, includeMainModule:=True, stack)
                        End While
 
                        stack.Free()
                    End SyncLock
                End If
 
                If SourceAssembly.CorLibrary IsNot Nothing Then
                    ' Add core library
                    AddUsedAssembly(sourceAssembly.CorLibrary)
                End If
            End If
 
            _usedAssemblyReferencesFrozen = True
        End Sub
 
        Friend Sub AddUsedAssemblies(assemblies As ICollection(Of AssemblySymbol))
            If Not assemblies.IsNullOrEmpty() Then
                For Each candidate In assemblies
                    AddUsedAssembly(candidate)
                Next
            End If
        End Sub
 
        Friend Function AddUsedAssembly(assembly As AssemblySymbol) As Boolean
 
            If assembly Is Nothing OrElse assembly Is SourceAssembly OrElse assembly.IsMissing Then
                Return False
            End If
 
            If _lazyUsedAssemblyReferences Is Nothing Then
                Interlocked.CompareExchange(_lazyUsedAssemblyReferences, New ConcurrentSet(Of AssemblySymbol)(), Nothing)
            End If
 
#If DEBUG Then
            Dim wasFrozen As Boolean = _usedAssemblyReferencesFrozen
#End If
            Dim added As Boolean = _lazyUsedAssemblyReferences.Add(assembly)
 
#If DEBUG Then
            Debug.Assert(Not added OrElse Not wasFrozen)
#End If
            Return added
        End Function
 
    End Class
End Namespace