File: Analysis\FlowAnalysis\VisualBasicDataFlowAnalysis.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
 
Namespace Microsoft.CodeAnalysis.VisualBasic
 
    ''' <summary>
    ''' This class implements the region data flow analysis operations.  Region data flow analysis provides information
    ''' how data flows into and out of a region.  The analysis is done lazily. When created, it performs no analysis, but
    ''' simply caches the arguments. Then, the first time one of the analysis results is used it computes that one
    ''' result and caches it. Each result is computed using a custom algorithm.
    ''' </summary>
    Friend Class VisualBasicDataFlowAnalysis
        Inherits DataFlowAnalysis
 
        Private ReadOnly _context As RegionAnalysisContext
 
        Private _variablesDeclared As ImmutableArray(Of ISymbol)
        Private _unassignedVariables As HashSet(Of Symbol)
        Private _dataFlowsIn As ImmutableArray(Of ISymbol)
        Private _definitelyAssignedOnEntry As ImmutableArray(Of ISymbol)
        Private _definitelyAssignedOnExit As ImmutableArray(Of ISymbol)
        Private _dataFlowsOut As ImmutableArray(Of ISymbol)
        Private _alwaysAssigned As ImmutableArray(Of ISymbol)
        Private _readInside As ImmutableArray(Of ISymbol)
        Private _writtenInside As ImmutableArray(Of ISymbol)
        Private _readOutside As ImmutableArray(Of ISymbol)
        Private _writtenOutside As ImmutableArray(Of ISymbol)
        Private _captured As ImmutableArray(Of ISymbol)
        Private _capturedInside As ImmutableArray(Of ISymbol)
        Private _capturedOutside As ImmutableArray(Of ISymbol)
        Private _succeeded As Boolean?
        Private _invalidRegionDetected As Boolean
 
        Friend Sub New(_context As RegionAnalysisContext)
            Me._context = _context
        End Sub
 
        ''' <summary>
        ''' A collection of the local variables that are declared within the region. Note that the region must be 
        ''' bounded by a method's body or a field's initializer, so parameter symbols are never included in the result.
        ''' </summary>
        Public Overrides ReadOnly Property VariablesDeclared As ImmutableArray(Of ISymbol)
            Get
                If _variablesDeclared.IsDefault Then
                    Dim result = If(Me._context.Failed, ImmutableArray(Of ISymbol).Empty,
                                    Normalize(VariablesDeclaredWalker.Analyze(_context.AnalysisInfo, _context.RegionInfo)))
                    ImmutableInterlocked.InterlockedCompareExchange(_variablesDeclared, result, Nothing)
                End If
                Return _variablesDeclared
            End Get
        End Property
 
        Private ReadOnly Property UnassignedVariables As HashSet(Of Symbol)
            Get
                If _unassignedVariables Is Nothing Then
                    Dim result = If(Me._context.Failed, New HashSet(Of Symbol)(),
                                    UnassignedVariablesWalker.Analyze(_context.AnalysisInfo))
                    Interlocked.CompareExchange(_unassignedVariables, result, Nothing)
                End If
                Return _unassignedVariables
            End Get
        End Property
 
        ''' <summary>
        ''' A collection of the local variables for which a value assigned outside the region may be used inside the region.
        ''' </summary>
        Public Overrides ReadOnly Property DataFlowsIn As ImmutableArray(Of ISymbol)
            Get
                If _dataFlowsIn.IsDefault Then
                    Me._succeeded = Not Me._context.Failed
                    Dim result = If(Me._context.Failed, ImmutableArray(Of ISymbol).Empty,
                                    Normalize(DataFlowsInWalker.Analyze(_context.AnalysisInfo, _context.RegionInfo, UnassignedVariables, _succeeded, _invalidRegionDetected)))
                    ImmutableInterlocked.InterlockedCompareExchange(_dataFlowsIn, result, Nothing)
                End If
                Return _dataFlowsIn
            End Get
        End Property
 
        Public Overrides ReadOnly Property DefinitelyAssignedOnEntry As ImmutableArray(Of ISymbol)
            Get
                Return ComputeDefinitelyAssignedValues().onEntry
            End Get
        End Property
 
        Public Overrides ReadOnly Property DefinitelyAssignedOnExit As ImmutableArray(Of ISymbol)
            Get
                Return ComputeDefinitelyAssignedValues().onExit
            End Get
        End Property
 
        Private Function ComputeDefinitelyAssignedValues() As (onEntry As ImmutableArray(Of ISymbol), onExit As ImmutableArray(Of ISymbol))
            ' Check for _definitelyAssignedOnExit as that's the last thing we write to. If it's not
            ' Default, then we'll have written to both variables and can safely read from either of
            ' them.
            If _definitelyAssignedOnExit.IsDefault Then
                Dim entry = ImmutableArray(Of ISymbol).Empty
                Dim ex = ImmutableArray(Of ISymbol).Empty
 
                Dim discarded = DataFlowsIn
                If Not Me._context.Failed Then
                    Dim tuple = DefinitelyAssignedWalker.Analyze(_context.AnalysisInfo, _context.RegionInfo)
                    entry = Normalize(tuple.entry)
                    ex = Normalize(tuple.ex)
                End If
 
                ImmutableInterlocked.InterlockedInitialize(_definitelyAssignedOnEntry, entry)
                ImmutableInterlocked.InterlockedInitialize(_definitelyAssignedOnExit, ex)
            End If
 
            Return (_definitelyAssignedOnEntry, _definitelyAssignedOnExit)
        End Function
 
        ''' <summary>
        ''' A collection of the local variables for which a value assigned inside the region may be used outside the region.
        ''' </summary>
        Public Overrides ReadOnly Property DataFlowsOut As ImmutableArray(Of ISymbol)
            Get
                Dim discarded = DataFlowsIn
                If _dataFlowsOut.IsDefault Then
                    Dim result = If(Me._context.Failed, ImmutableArray(Of ISymbol).Empty,
                                    Normalize(DataFlowsOutWalker.Analyze(_context.AnalysisInfo, _context.RegionInfo, UnassignedVariables, _dataFlowsIn)))
                    ImmutableInterlocked.InterlockedCompareExchange(_dataFlowsOut, result, Nothing)
                End If
                Return _dataFlowsOut
            End Get
        End Property
 
        ''' <summary>
        ''' A collection of the local variables for which a value is always assigned inside the region.
        ''' </summary>
        Public Overrides ReadOnly Property AlwaysAssigned As ImmutableArray(Of ISymbol)
            Get
                If _alwaysAssigned.IsDefault Then
                    Dim result = If(Me._context.Failed, ImmutableArray(Of ISymbol).Empty,
                                    Normalize(AlwaysAssignedWalker.Analyze(_context.AnalysisInfo, _context.RegionInfo)))
                    ImmutableInterlocked.InterlockedCompareExchange(_alwaysAssigned, result, Nothing)
                End If
                Return _alwaysAssigned
            End Get
        End Property
 
        ''' <summary>
        ''' A collection of the local variables that are read inside the region.
        ''' </summary>
        Public Overrides ReadOnly Property ReadInside As ImmutableArray(Of ISymbol)
            Get
                If _readInside.IsDefault Then
                    AnalyzeReadWrite()
                End If
                Return _readInside
            End Get
        End Property
 
        ''' <summary>
        ''' A collection of local variables that are written inside the region.
        ''' </summary>
        Public Overrides ReadOnly Property WrittenInside As ImmutableArray(Of ISymbol)
            Get
                If _writtenInside.IsDefault Then
                    AnalyzeReadWrite()
                End If
                Return _writtenInside
            End Get
        End Property
 
        ''' <summary>
        ''' A collection of the local variables that are read outside the region.
        ''' </summary>
        Public Overrides ReadOnly Property ReadOutside As ImmutableArray(Of ISymbol)
            Get
                If _readOutside.IsDefault Then
                    AnalyzeReadWrite()
                End If
                Return _readOutside
            End Get
        End Property
 
        ''' <summary>
        ''' A collection of local variables that are written inside the region.
        ''' </summary>
        Public Overrides ReadOnly Property WrittenOutside As ImmutableArray(Of ISymbol)
            Get
                If _writtenOutside.IsDefault Then
                    AnalyzeReadWrite()
                End If
                Return _writtenOutside
            End Get
        End Property
 
        Private Sub AnalyzeReadWrite()
            Dim readInside As IEnumerable(Of Symbol) = Nothing
            Dim writtenInside As IEnumerable(Of Symbol) = Nothing
            Dim readOutside As IEnumerable(Of Symbol) = Nothing
            Dim writtenOutside As IEnumerable(Of Symbol) = Nothing
            Dim captured As IEnumerable(Of Symbol) = Nothing
            Dim capturedInside As IEnumerable(Of Symbol) = Nothing
            Dim capturedOutside As IEnumerable(Of Symbol) = Nothing
 
            If Not Me.Succeeded Then
                readInside = Enumerable.Empty(Of Symbol)()
                writtenInside = readInside
                readOutside = readInside
                writtenOutside = readInside
                captured = readInside
                capturedInside = readInside
                capturedOutside = readInside
            Else
                ReadWriteWalker.Analyze(
                    _context.AnalysisInfo, _context.RegionInfo,
                    readInside:=readInside,
                    writtenInside:=writtenInside,
                    readOutside:=readOutside,
                    writtenOutside:=writtenOutside,
                    captured:=captured,
                    capturedInside:=capturedInside,
                    capturedOutside:=capturedOutside)
            End If
 
            ImmutableInterlocked.InterlockedCompareExchange(Me._readInside, Normalize(readInside), Nothing)
            ImmutableInterlocked.InterlockedCompareExchange(Me._writtenInside, Normalize(writtenInside), Nothing)
            ImmutableInterlocked.InterlockedCompareExchange(Me._readOutside, Normalize(readOutside), Nothing)
            ImmutableInterlocked.InterlockedCompareExchange(Me._writtenOutside, Normalize(writtenOutside), Nothing)
            ImmutableInterlocked.InterlockedCompareExchange(Me._captured, Normalize(captured), Nothing)
            ImmutableInterlocked.InterlockedCompareExchange(Me._capturedInside, Normalize(capturedInside), Nothing)
            ImmutableInterlocked.InterlockedCompareExchange(Me._capturedOutside, Normalize(capturedOutside), Nothing)
        End Sub
 
        ''' <summary>
        ''' A collection of the local variables that have been referenced in anonymous functions
        ''' and therefore must be moved to a field of a frame class.
        ''' </summary>
        Public Overrides ReadOnly Property Captured As ImmutableArray(Of ISymbol)
            Get
                If Me._captured.IsDefault Then
                    AnalyzeReadWrite()
                End If
 
                Return Me._captured
            End Get
        End Property
 
        Public Overrides ReadOnly Property CapturedInside As ImmutableArray(Of ISymbol)
            Get
                If Me._capturedInside.IsDefault Then
                    AnalyzeReadWrite()
                End If
 
                Return Me._capturedInside
            End Get
        End Property
 
        Public Overrides ReadOnly Property CapturedOutside As ImmutableArray(Of ISymbol)
            Get
                If Me._capturedOutside.IsDefault Then
                    AnalyzeReadWrite()
                End If
 
                Return Me._capturedOutside
            End Get
        End Property
 
        Friend ReadOnly Property InvalidRegionDetectedInternal As Boolean
            Get
                Return If(Me.Succeeded, False, Me._invalidRegionDetected)
            End Get
        End Property
 
        Public NotOverridable Overrides ReadOnly Property Succeeded As Boolean
            Get
                If Me._succeeded Is Nothing Then
                    Dim discarded = DataFlowsIn
                End If
 
                Return Me._succeeded.Value
            End Get
        End Property
 
        Public Overrides ReadOnly Property UnsafeAddressTaken As ImmutableArray(Of ISymbol)
            Get
                Return ImmutableArray(Of ISymbol).Empty
            End Get
        End Property
 
        Public Overrides ReadOnly Property UsedLocalFunctions As ImmutableArray(Of IMethodSymbol)
            Get
                Return ImmutableArray(Of IMethodSymbol).Empty
            End Get
        End Property
 
        Friend Function Normalize(data As IEnumerable(Of Symbol)) As ImmutableArray(Of ISymbol)
            Dim builder = ArrayBuilder(Of Symbol).GetInstance()
            builder.AddRange(data.Where(Function(s) s.CanBeReferencedByName))
            builder.Sort(LexicalOrderSymbolComparer.Instance)
            Return builder.ToImmutableAndFree().As(Of ISymbol)()
        End Function
    End Class
 
End Namespace