|
' 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.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
' NOTE: VB does not support constant expressions in flow analysis during command-line compilation, but supports them when
' analysis is being called via public API. This distinction is governed by 'suppressConstantExpressions' flag
Namespace Microsoft.CodeAnalysis.VisualBasic
Partial Friend MustInherit Class AbstractFlowPass(Of LocalState As AbstractLocalState)
Inherits BoundTreeVisitor
''' <summary>
''' The compilation in which the analysis is taking place. This is needed to determine which
''' conditional methods will be compiled and which will be omitted.
''' </summary>
Protected ReadOnly compilation As VisualBasicCompilation
''' <summary>
''' The symbol of method whose body is being analyzed or field or property whose
''' initializer is being analyzed
''' </summary>
Public symbol As Symbol
''' <summary>
''' The bound code of the method or initializer being analyzed
''' </summary>
Private ReadOnly _methodOrInitializerMainNode As BoundNode
''' <summary>
''' The flow analysis state at each label, computed by merging the state from branches to
''' that label with the state when we fall into the label. Entries are created when the
''' label is encountered. One case deserves special attention: when the destination of the
''' branch is a label earlier in the code, it is possible (though rarely occurs in practice)
''' that we are changing the state at a label that we've already analyzed. In that case we
''' run another pass of the analysis to allow those changes to propagate. This repeats until
''' no further changes to the state of these labels occurs. This can result in quadratic
''' performance in unlikely but possible code such as this: "int x; if (cond) goto l1; x =
''' 3; l5: print x; l4: goto l5; l3: goto l4; l2: goto l3; l1: goto l2;"
''' </summary>
Private ReadOnly _labels As New Dictionary(Of LabelSymbol, LabelStateAndNesting)
''' <summary> All of the labels seen so far in this forward scan of the body </summary>
Private _labelsSeen As New HashSet(Of LabelSymbol)
Private _placeholderReplacementMap As Dictionary(Of BoundValuePlaceholderBase, BoundExpression)
''' <summary>
''' Set to true after an analysis scan if the analysis was incomplete due to a backward
''' "goto" branch changing some analysis result. In this case the caller scans again (until
''' this is false). Since the analysis proceeds by monotonically changing the state computed
''' at each label, this must terminate.
''' </summary>
Friend backwardBranchChanged As Boolean = False
''' <summary> Actual storage for PendingBranches </summary>
Private _pendingBranches As ArrayBuilder(Of PendingBranch) = ArrayBuilder(Of PendingBranch).GetInstance()
''' <summary> The definite assignment and/or reachability state at the point currently being analyzed. </summary>
Protected State As LocalState
Protected StateWhenTrue As LocalState
Protected StateWhenFalse As LocalState
Protected IsConditionalState As Boolean
''' <summary>
''' 'Me' parameter, relevant for methods, fields, properties, otherwise Nothing
''' </summary>
Protected ReadOnly MeParameter As ParameterSymbol
''' <summary>
''' Used only in the data flows out walker, we track unassignments as well as assignments
''' </summary>
Protected ReadOnly TrackUnassignments As Boolean
''' <summary>
''' The current lexical nesting in the BoundTree.
''' </summary>
''' <remarks></remarks>
Private _nesting As ArrayBuilder(Of Integer)
''' <summary>
''' Where all diagnostics are deposited.
''' </summary>
Protected ReadOnly diagnostics As DiagnosticBag = DiagnosticBag.GetInstance()
''' <summary> Indicates whether or not support of constant expressions (boolean and nothing)
''' is enabled in this analyzer. In general, constant expressions support is enabled in analysis
''' exposed to public API consumer and disabled when used from command-line compiler. </summary>
Private ReadOnly _suppressConstantExpressions As Boolean
Protected _recursionDepth As Integer
''' <summary>
''' Construct an object for outside-region analysis
''' </summary>
Protected Sub New(_info As FlowAnalysisInfo, suppressConstExpressionsSupport As Boolean)
MyClass.New(_info, Nothing, suppressConstExpressionsSupport, False)
End Sub
''' <summary>
''' Construct an object for region-aware analysis
''' </summary>
Protected Sub New(_info As FlowAnalysisInfo, _region As FlowAnalysisRegionInfo, suppressConstExpressionsSupport As Boolean, trackUnassignments As Boolean)
Debug.Assert(_info.Symbol.Kind = SymbolKind.Field OrElse
_info.Symbol.Kind = SymbolKind.Property OrElse
_info.Symbol.Kind = SymbolKind.Method OrElse
_info.Symbol.Kind = SymbolKind.Parameter)
Me.compilation = _info.Compilation
Me.symbol = _info.Symbol
Me.MeParameter = Me.symbol.GetMeParameter()
Me._methodOrInitializerMainNode = _info.Node
Me._firstInRegion = _region.FirstInRegion
Me._lastInRegion = _region.LastInRegion
Me._region = _region.Region
Me.TrackUnassignments = trackUnassignments
Me._loopHeadState = If(trackUnassignments, New Dictionary(Of BoundLoopStatement, LocalState)(), Nothing)
Me._suppressConstantExpressions = suppressConstExpressionsSupport
End Sub
Protected Overridable Sub InitForScan()
End Sub
Protected MustOverride Function ReachableState() As LocalState
Protected MustOverride Function UnreachableState() As LocalState
''' <summary> Set conditional state </summary>
Private Sub SetConditionalState(_whenTrue As LocalState, _whenFalse As LocalState)
Me.State = Nothing
Me.StateWhenTrue = _whenTrue
Me.StateWhenFalse = _whenFalse
Me.IsConditionalState = True
End Sub
''' <summary> Set unconditional state </summary>
Protected Sub SetState(_state As LocalState)
Me.State = _state
If Me.IsConditionalState Then
Me.StateWhenTrue = Nothing
Me.StateWhenFalse = Nothing
Me.IsConditionalState = False
End If
End Sub
''' <summary> Split state </summary>
Protected Sub Split()
If Not Me.IsConditionalState Then
SetConditionalState(Me.State, Me.State.Clone())
End If
End Sub
''' <summary> Intersect and unsplit state </summary>
Protected Sub Unsplit()
If Me.IsConditionalState Then
Me.IntersectWith(Me.StateWhenTrue, Me.StateWhenFalse)
Me.SetState(Me.StateWhenTrue)
End If
End Sub
''' <summary>
''' Pending escapes generated in the current scope (or more deeply nested scopes). When jump
''' statements (goto, break, continue, return) are processed, they are placed in the
''' Me._pendingBranches buffer to be processed later by the code handling the destination
''' statement. As a special case, the processing of try-finally statements might modify the
''' contents of the Me._pendingBranches buffer to take into account the behavior of
''' "intervening" finally clauses.
''' </summary>
Protected ReadOnly Property PendingBranches As ImmutableArray(Of PendingBranch)
Get
Return _pendingBranches.ToImmutable
End Get
End Property
''' <summary>
''' Perform a single pass of flow analysis. Note that after this pass,
''' this.backwardBranchChanged indicates if a further pass is required.
''' </summary>
''' <returns>False if the region is invalid</returns>
Protected Overridable Function Scan() As Boolean
' Clear diagnostics reported in the previous iteration
Me.diagnostics.Clear()
' initialize
Me._regionPlace = RegionPlace.Before
Me.SetState(ReachableState())
Me.backwardBranchChanged = False
If Me._nesting IsNot Nothing Then
Me._nesting.Free()
End If
Me._nesting = ArrayBuilder(Of Integer).GetInstance()
InitForScan()
' pending branches should be restored after each iteration
Dim oldPending As SavedPending = Me.SavePending()
Visit(Me._methodOrInitializerMainNode)
Me.RestorePending(oldPending)
Me._labelsSeen.Clear()
' if we are tracking regions, we must have left the region by now;
' otherwise the region was erroneous which must have been detected earlier
Return Me._firstInRegion Is Nothing OrElse Me._regionPlace = RegionPlace.After
End Function
''' <returns>False if the region is invalid</returns>
Protected Overridable Function Analyze() As Boolean
Do
If Not Me.Scan() Then
Return False
End If
Loop While Me.backwardBranchChanged
Return True
End Function
Protected Overridable Sub Free()
If Me._nesting IsNot Nothing Then
Me._nesting.Free()
End If
Me.diagnostics.Free()
Me._pendingBranches.Free()
End Sub
''' <summary>
''' If analysis is being performed in a context of a method returns method's parameters,
''' otherwise returns an empty array
''' </summary>
Protected ReadOnly Property MethodParameters As ImmutableArray(Of ParameterSymbol)
Get
Return If(Me.symbol.Kind = SymbolKind.Method, DirectCast(Me.symbol, MethodSymbol).Parameters, ImmutableArray(Of ParameterSymbol).Empty)
End Get
End Property
''' <summary>
''' Specifies whether or not method's ByRef parameters should be analyzed. If there's more than one location in
''' the method being analyzed, then the method is partial and we prefer to report an out parameter in partial
''' method error.
''' Note: VB doesn't support "out" so it doesn't warn for unassigned parameters. However, check variables passed
''' byref are assigned so that data flow analysis detects parameters flowing out.
''' </summary>
''' <returns>true if the out parameters of the method should be analyzed</returns>
Protected ReadOnly Property ShouldAnalyzeByRefParameters As Boolean
Get
Return Me.symbol.Kind = SymbolKind.Method AndAlso DirectCast(Me.symbol, MethodSymbol).Locations.Length = 1
End Get
End Property
''' <summary>
''' Method symbol or nothing
''' TODO: Need to try and get rid of this property
''' </summary>
Protected ReadOnly Property MethodSymbol As MethodSymbol
Get
Return If(Me.symbol.Kind = SymbolKind.Method, DirectCast(Me.symbol, MethodSymbol), Nothing)
End Get
End Property
''' <summary>
''' If analysis is being performed in a context of a method returns method's return type,
''' otherwise returns Nothing
''' </summary>
Protected ReadOnly Property MethodReturnType As TypeSymbol
Get
Return If(Me.symbol.Kind = SymbolKind.Method, DirectCast(Me.symbol, MethodSymbol).ReturnType, Nothing)
End Get
End Property
''' <summary>
''' Return the flow analysis state associated with a label.
''' </summary>
''' <param name="label"></param>
''' <returns></returns>
''' <remarks></remarks>
Private Function LabelState(label As LabelSymbol) As LocalState
Dim result As LabelStateAndNesting = Nothing
If _labels.TryGetValue(label, result) Then
Return result.State
End If
Return UnreachableState()
End Function
''' <summary>
''' Set the current state to one that indicates that it is unreachable.
''' </summary>
Protected Sub SetUnreachable()
Me.SetState(UnreachableState())
End Sub
Private Function IsConstantTrue(node As BoundExpression) As Boolean
If Me._suppressConstantExpressions Then
Return False
End If
If Not node.IsConstant Then
Return False
End If
Dim constantValue = node.ConstantValueOpt
If constantValue.Discriminator <> ConstantValueTypeDiscriminator.Boolean Then
Return False
End If
Return constantValue.BooleanValue
End Function
Private Function IsConstantFalse(node As BoundExpression) As Boolean
If Me._suppressConstantExpressions Then
Return False
End If
If Not node.IsConstant Then
Return False
End If
Dim constantValue = node.ConstantValueOpt
If constantValue.Discriminator <> ConstantValueTypeDiscriminator.Boolean Then
Return False
End If
Return Not constantValue.BooleanValue
End Function
Protected Shared Function IsNonPrimitiveValueType(type As TypeSymbol) As Boolean
Debug.Assert(type IsNot Nothing)
If Not type.IsValueType Then
Return False
End If
Select Case type.SpecialType
Case SpecialType.System_Void,
SpecialType.System_Boolean,
SpecialType.System_Char,
SpecialType.System_SByte,
SpecialType.System_Byte,
SpecialType.System_Int16,
SpecialType.System_UInt16,
SpecialType.System_Int32,
SpecialType.System_UInt32,
SpecialType.System_Int64,
SpecialType.System_UInt64,
SpecialType.System_Decimal,
SpecialType.System_Single,
SpecialType.System_Double,
SpecialType.System_DateTime,
SpecialType.System_TypedReference,
SpecialType.System_ArgIterator,
SpecialType.System_RuntimeArgumentHandle,
SpecialType.System_RuntimeFieldHandle,
SpecialType.System_RuntimeMethodHandle,
SpecialType.System_RuntimeTypeHandle
Return False
Case Else
Return True
End Select
End Function
''' <summary>
''' Called at the point in a loop where the backwards branch would go to.
''' </summary>
''' <param name="node"></param>
''' <remarks></remarks>
Private Sub LoopHead(node As BoundLoopStatement)
If Me.TrackUnassignments Then
Dim previousState As LocalState
If Me._loopHeadState.TryGetValue(node, previousState) Then
IntersectWith(Me.State, previousState)
End If
Me._loopHeadState(node) = Me.State.Clone()
End If
End Sub
''' <summary>
''' Called at the point in a loop where the backward branch is placed.
''' </summary>
''' <param name="node"></param>
''' <remarks></remarks>
Private Sub LoopTail(node As BoundLoopStatement)
If Me.TrackUnassignments Then
Dim oldState = Me._loopHeadState(node)
If IntersectWith(oldState, Me.State) Then
Me._loopHeadState(node) = oldState
Me.backwardBranchChanged = True
End If
End If
End Sub
''' <summary>
''' Used to resolve exit statements in each statement form that has an Exit statement
''' (loops, switch).
''' </summary>
Private Sub ResolveBreaks(breakState As LocalState, breakLabel As LabelSymbol)
Dim newPendingBranches = ArrayBuilder(Of PendingBranch).GetInstance()
For Each pending In Me.PendingBranches
Select Case pending.Branch.Kind
Case BoundKind.ExitStatement
Dim exitStmt = TryCast(pending.Branch, BoundExitStatement)
If exitStmt.Label = breakLabel Then
IntersectWith(breakState, pending.State)
Else
' If it doesn't match then it is for an outer block
newPendingBranches.Add(pending)
End If
Case Else
newPendingBranches.Add(pending)
End Select
Next
ResetPendingBranches(newPendingBranches)
Me.SetState(breakState)
End Sub
''' <summary>
''' Used to resolve continue statements in each statement form that supports it.
''' </summary>
''' <param name = "continueLabel"></param>
Private Sub ResolveContinues(continueLabel As LabelSymbol)
Dim newPendingBranches = ArrayBuilder(Of PendingBranch).GetInstance()
For Each pending In Me.PendingBranches
Select Case pending.Branch.Kind
Case BoundKind.ContinueStatement
' When the continue XXX does not match an enclosing XXX block then no label exists
Dim continueStmt = TryCast(pending.Branch, BoundContinueStatement)
' Technically, nothing in the language specification depends on the state
' at the continue label, so we could just discard them instead of merging
' the states. In fact, we need not have added continue statements to the
' pending jump queue in the first place if we were interested solely in the
' flow analysis. However, region analysis (in support of extract method)
' depends on continue statements appearing in the pending branch queue, so
' we process them from the queue here.
If continueStmt.Label = continueLabel Then
IntersectWith(Me.State, pending.State)
Else
' If it doesn't match then it is for an outer block
newPendingBranches.Add(pending)
End If
Case Else
newPendingBranches.Add(pending)
End Select
Next
ResetPendingBranches(newPendingBranches)
End Sub
''' <summary>
''' Subclasses override this if they want to take special actions on processing a goto
''' statement, when both the jump and the label have been located.
''' </summary>
Protected Overridable Sub NoteBranch(pending As PendingBranch, stmt As BoundStatement, labelStmt As BoundLabelStatement)
End Sub
Private Shared Function GetBranchTargetLabel(branch As BoundStatement, gotoOnly As Boolean) As LabelSymbol
Select Case branch.Kind
Case BoundKind.GotoStatement
Return DirectCast(branch, BoundGotoStatement).Label
Case BoundKind.ConditionalGoto
Return DirectCast(branch, BoundConditionalGoto).Label
Case BoundKind.ExitStatement
Return If(gotoOnly, Nothing, DirectCast(branch, BoundExitStatement).Label)
Case BoundKind.ReturnStatement
Return If(gotoOnly, Nothing, DirectCast(branch, BoundReturnStatement).ExitLabelOpt)
Case BoundKind.ContinueStatement
Return Nothing
Case BoundKind.YieldStatement
Return Nothing
Case Else
Throw ExceptionUtilities.UnexpectedValue(branch.Kind)
End Select
Return Nothing
End Function
Protected Overridable Sub ResolveBranch(pending As PendingBranch, label As LabelSymbol, target As BoundLabelStatement, ByRef labelStateChanged As Boolean)
Dim _state = LabelState(target.Label)
NoteBranch(pending, pending.Branch, target)
Dim changed = IntersectWith(_state, pending.State)
If changed Then
labelStateChanged = True
Me._labels(target.Label) = New LabelStateAndNesting(target, _state, Me._nesting)
End If
End Sub
''' <summary>
''' To handle a label, we resolve all pending forward references to branches to that label. Returns true if the state of
''' the label changes as a result.
''' </summary>
''' <param name = "target"></param>
''' <returns></returns>
Private Function ResolveBranches(target As BoundLabelStatement) As Boolean
Dim labelStateChanged As Boolean = False
If Me.PendingBranches.Length > 0 Then
Dim newPendingBranches = ArrayBuilder(Of PendingBranch).GetInstance()
For Each pending In Me.PendingBranches
Dim label As LabelSymbol = GetBranchTargetLabel(pending.Branch, False)
If label IsNot Nothing AndAlso label = target.Label Then
ResolveBranch(pending, label, target, labelStateChanged)
Else
newPendingBranches.Add(pending)
End If
Next
ResetPendingBranches(newPendingBranches)
End If
Return labelStateChanged
End Function
Protected Class SavedPending
Public ReadOnly PendingBranches As ArrayBuilder(Of PendingBranch)
Public ReadOnly LabelsSeen As HashSet(Of LabelSymbol)
Public Sub New(ByRef _pendingBranches As ArrayBuilder(Of PendingBranch), ByRef _labelsSeen As HashSet(Of LabelSymbol))
Me.PendingBranches = _pendingBranches
Me.LabelsSeen = _labelsSeen
_pendingBranches = ArrayBuilder(Of PendingBranch).GetInstance()
_labelsSeen = New HashSet(Of LabelSymbol)
End Sub
End Class
''' <summary>
''' When branching into constructs that don't support jumps into/out of (i.e. lambdas),
''' we save the pending branches when visiting more nested constructs.
''' </summary>
Protected Function SavePending() As SavedPending
Return New SavedPending(Me._pendingBranches, Me._labelsSeen)
End Function
Private Sub ResetPendingBranches(newPendingBranches As ArrayBuilder(Of PendingBranch))
Debug.Assert(newPendingBranches IsNot Nothing)
Debug.Assert(newPendingBranches IsNot Me._pendingBranches)
Me._pendingBranches.Free()
Me._pendingBranches = newPendingBranches
End Sub
''' <summary>
''' We use this to restore the old set of pending branches and labels after visiting a construct that contains nested statements.
''' </summary>
''' <param name="oldPending">The old pending branches/labels, which are to be merged with the current ones</param>
Protected Sub RestorePending(oldPending As SavedPending, Optional mergeLabelsSeen As Boolean = False)
If ResolveBranches(Me._labelsSeen) Then
Me.backwardBranchChanged = True
End If
oldPending.PendingBranches.AddRange(Me.PendingBranches)
ResetPendingBranches(oldPending.PendingBranches)
' We only use SavePending/RestorePending when there could be no branch into the region between them.
' So there is no need to save the labels seen between the calls. If there were such a need, we would
' do "this.labelsSeen.UnionWith(oldPending.LabelsSeen);" instead of the following assignment
If mergeLabelsSeen Then
Me._labelsSeen.AddAll(oldPending.LabelsSeen)
Else
Me._labelsSeen = oldPending.LabelsSeen
End If
End Sub
''' <summary>
''' We look at all pending branches and attempt to resolve the branches with labels if the nesting of the
''' block is the nearest common parent to the branch and the label. Because the code is evaluated recursively
''' outward we only need to check if the current nesting is a prefix of both the branch and the label nesting.
''' </summary>
Private Function ResolveBranches(labelsFilter As HashSet(Of LabelSymbol)) As Boolean
Dim labelStateChanged As Boolean = False
If Me.PendingBranches.Length > 0 Then
Dim newPendingBranches = ArrayBuilder(Of PendingBranch).GetInstance()
For Each pending In Me.PendingBranches
Dim labelSymbol As LabelSymbol = Nothing
Dim labelAndNesting As LabelStateAndNesting = Nothing
If BothBranchAndLabelArePrefixedByNesting(pending, labelsFilter, labelSymbol:=labelSymbol, labelAndNesting:=labelAndNesting) Then
Dim changed As Boolean
ResolveBranch(pending, labelSymbol, labelAndNesting.Target, changed)
If changed Then
labelStateChanged = True
End If
Continue For
End If
newPendingBranches.Add(pending)
Next
ResetPendingBranches(newPendingBranches)
End If
Return labelStateChanged
End Function
Private Function BothBranchAndLabelArePrefixedByNesting(branch As PendingBranch,
Optional labelsFilter As HashSet(Of LabelSymbol) = Nothing,
Optional ignoreLast As Boolean = False,
<Out()> Optional ByRef labelSymbol As LabelSymbol = Nothing,
<Out()> Optional ByRef labelAndNesting As LabelStateAndNesting = Nothing) As Boolean
Dim branchStatement As BoundStatement = branch.Branch
If branchStatement IsNot Nothing AndAlso branch.Nesting.IsPrefixedBy(Me._nesting, ignoreLast) Then
labelSymbol = GetBranchTargetLabel(branchStatement, gotoOnly:=True)
If labelSymbol IsNot Nothing AndAlso (labelsFilter Is Nothing OrElse labelsFilter.Contains(labelSymbol)) Then
Return Me._labels.TryGetValue(labelSymbol, labelAndNesting) AndAlso
labelAndNesting.Nesting.IsPrefixedBy(Me._nesting, ignoreLast)
End If
End If
Return False
End Function
''' <summary>
''' Report an unimplemented language construct.
''' </summary>
''' <param name = "node"></param>
''' <param name = "feature"></param>
''' <returns></returns>
Protected Overridable Function Unimplemented(node As BoundNode, feature As [String]) As BoundNode
Return Nothing
End Function
Protected Overridable Function AllBitsSet() As LocalState
Return Nothing
End Function
Protected Sub SetPlaceholderSubstitute(placeholder As BoundValuePlaceholderBase, newSubstitute As BoundExpression)
Debug.Assert(placeholder IsNot Nothing)
If _placeholderReplacementMap Is Nothing Then
_placeholderReplacementMap = New Dictionary(Of BoundValuePlaceholderBase, BoundExpression)()
End If
Debug.Assert(Not _placeholderReplacementMap.ContainsKey(placeholder))
_placeholderReplacementMap(placeholder) = newSubstitute
End Sub
Protected Sub RemovePlaceholderSubstitute(placeholder As BoundValuePlaceholderBase)
Debug.Assert(placeholder IsNot Nothing)
Debug.Assert(_placeholderReplacementMap.ContainsKey(placeholder))
Me._placeholderReplacementMap.Remove(placeholder)
End Sub
Protected ReadOnly Property GetPlaceholderSubstitute(placeholder As BoundValuePlaceholderBase) As BoundExpression
Get
Dim value As BoundExpression = Nothing
If Me._placeholderReplacementMap IsNot Nothing AndAlso Me._placeholderReplacementMap.TryGetValue(placeholder, value) Then
Return value
End If
Return Nothing
End Get
End Property
Protected MustOverride Function Dump(state As LocalState) As String
#Region "Visitors"
Public NotOverridable Overrides Function Visit(node As BoundNode) As BoundNode
Visit(node, dontLeaveRegion:=False)
Return Nothing
End Function
''' <summary>
''' Visit a node.
''' </summary>
Protected Overridable Overloads Sub Visit(node As BoundNode, dontLeaveRegion As Boolean)
VisitAlways(node, dontLeaveRegion:=dontLeaveRegion)
End Sub
''' <summary>
''' Visit a node, process
''' </summary>
Protected Sub VisitAlways(node As BoundNode, Optional dontLeaveRegion As Boolean = False)
If Me._firstInRegion Is Nothing Then
VisitWithStackGuard(node)
Else
If node Is Me._firstInRegion AndAlso Me._regionPlace = RegionPlace.Before Then
Me.EnterRegion()
End If
VisitWithStackGuard(node)
If Not dontLeaveRegion AndAlso node Is Me._lastInRegion AndAlso IsInside Then
Me.LeaveRegion()
End If
End If
End Sub
<DebuggerStepThrough>
Private Shadows Function VisitWithStackGuard(node As BoundNode) As BoundNode
Dim expression = TryCast(node, BoundExpression)
If expression IsNot Nothing Then
Return VisitExpressionWithStackGuard(_recursionDepth, expression)
End If
Return MyBase.Visit(node)
End Function
<DebuggerStepThrough>
Protected Overrides Function VisitExpressionWithoutStackGuard(node As BoundExpression) As BoundExpression
Return DirectCast(MyBase.Visit(node), BoundExpression)
End Function
Protected Overrides Function ConvertInsufficientExecutionStackExceptionToCancelledByStackGuardException() As Boolean
Return False ' just let the original exception to bubble up.
End Function
Protected Overridable Sub VisitLvalue(node As BoundExpression, Optional dontLeaveRegion As Boolean = False)
' NOTE: we can skip checking if Me._firstInRegion is nothing because 'node' is not nothing
If node Is Me._firstInRegion AndAlso Me._regionPlace = RegionPlace.Before Then
Me.EnterRegion()
End If
Select Case node.Kind
Case BoundKind.Local
Dim local = DirectCast(node, BoundLocal)
If local.LocalSymbol.IsByRef Then
VisitRvalue(local)
End If
Case BoundKind.Parameter, BoundKind.MeReference, BoundKind.MyClassReference, BoundKind.MyBaseReference
' no need for it to be previously assigned: it is on the left.
Case BoundKind.FieldAccess
VisitFieldAccessInternal(DirectCast(node, BoundFieldAccess))
Case BoundKind.WithLValueExpressionPlaceholder,
BoundKind.WithRValueExpressionPlaceholder,
BoundKind.LValuePlaceholder,
BoundKind.RValuePlaceholder
' TODO: Other placeholder kinds?
Dim substitute As BoundExpression = GetPlaceholderSubstitute(DirectCast(node, BoundValuePlaceholderBase))
If substitute IsNot Nothing Then
VisitLvalue(substitute)
Else
VisitRvalue(node, dontLeaveRegion:=True)
End If
Case Else
VisitRvalue(node, dontLeaveRegion:=True)
End Select
' NOTE: we can skip checking if Me._firstInRegion is nothing because 'node' is not nothing
If Not dontLeaveRegion AndAlso node Is Me._lastInRegion AndAlso IsInside Then
Me.LeaveRegion()
End If
End Sub
''' <summary>
''' Visit a boolean condition expression.
''' </summary>
Protected Sub VisitCondition(node As BoundExpression)
' NOTE: we can skip checking if Me._firstInRegion is nothing because 'node' is not nothing
If node Is Me._firstInRegion AndAlso Me._regionPlace = RegionPlace.Before Then
Me.EnterRegion()
End If
Visit(node, dontLeaveRegion:=True)
AdjustConditionalState(node)
' NOTE: we can skip checking if Me._firstInRegion is nothing because 'node' is not nothing
If node Is Me._lastInRegion AndAlso IsInside Then
Me.LeaveRegion()
End If
End Sub
Private Sub AdjustConditionalState(node As BoundExpression)
If IsConstantTrue(node) Then
Me.Unsplit()
Me.SetConditionalState(Me.State, UnreachableState())
ElseIf IsConstantFalse(node) Then
Me.Unsplit()
Me.SetConditionalState(UnreachableState(), Me.State)
Else
Me.Split()
End If
End Sub
''' <summary>
''' Visit a general expression, where we will only need to determine if variables are
''' assigned (or not). That is, we will not be needing AssignedWhenTrue and
''' AssignedWhenFalse.
''' </summary>
Protected Sub VisitRvalue(node As BoundExpression, Optional rwContext As ReadWriteContext = ReadWriteContext.None, Optional dontLeaveRegion As Boolean = False)
' NOTE: we can skip checking if Me._firstInRegion is nothing because 'node' is not nothing
If node Is Me._firstInRegion AndAlso Me._regionPlace = RegionPlace.Before Then
Me.EnterRegion()
End If
If rwContext <> ReadWriteContext.None Then
Select Case node.Kind
Case BoundKind.Local
VisitLocalInReadWriteContext(DirectCast(node, BoundLocal), rwContext)
GoTo lUnsplitAndFinish
Case BoundKind.FieldAccess
VisitFieldAccessInReadWriteContext(DirectCast(node, BoundFieldAccess), rwContext)
GoTo lUnsplitAndFinish
End Select
End If
Visit(node, dontLeaveRegion:=True)
lUnsplitAndFinish:
Me.Unsplit()
' NOTE: we can skip checking if Me._firstInRegion is nothing because 'node' is not nothing
If Not dontLeaveRegion AndAlso node Is Me._lastInRegion AndAlso IsInside Then
Me.LeaveRegion()
End If
End Sub
Public Overrides Function VisitSequence(node As BoundSequence) As BoundNode
Dim sideEffects = node.SideEffects
If Not sideEffects.IsEmpty Then
For Each sideEffect In node.SideEffects
VisitExpressionAsStatement(sideEffect)
Next
End If
Debug.Assert(node.ValueOpt IsNot Nothing OrElse node.HasErrors OrElse node.Type.SpecialType = SpecialType.System_Void)
If node.ValueOpt IsNot Nothing Then
VisitRvalue(node.ValueOpt)
End If
Return Nothing
End Function
Public Overrides Function VisitByRefArgumentPlaceholder(node As BoundByRefArgumentPlaceholder) As BoundNode
Dim replacement As BoundExpression = GetPlaceholderSubstitute(node)
If replacement IsNot Nothing Then
VisitRvalue(replacement, ReadWriteContext.ByRefArgument)
End If
Return Nothing
End Function
Public Overrides Function VisitByRefArgumentWithCopyBack(node As BoundByRefArgumentWithCopyBack) As BoundNode
Me.SetPlaceholderSubstitute(node.InPlaceholder, node.OriginalArgument)
VisitRvalue(node.InConversion)
Me.RemovePlaceholderSubstitute(node.InPlaceholder)
Return Nothing
End Function
Protected Overridable Sub VisitStatement(statement As BoundStatement)
Visit(statement)
End Sub
Public Overrides Function VisitLValueToRValueWrapper(node As BoundLValueToRValueWrapper) As BoundNode
Visit(node.UnderlyingLValue)
Return Nothing
End Function
Public Overrides Function VisitWithLValueExpressionPlaceholder(node As BoundWithLValueExpressionPlaceholder) As BoundNode
Visit(GetPlaceholderSubstitute(node))
Return Nothing
End Function
Public Overrides Function VisitWithStatement(node As BoundWithStatement) As BoundNode
' In case we perform _region_ flow analysis processing of the region inside With
' statement expression is a little subtle. The original expression may be
' rewritten into a placeholder substitution expression and a set of initializers.
' The placeholder will be processed when we visit left-omitted part of member or
' dictionary access in the statements inside the With statement body, but the
' initializers are to be processed with BoundWithStatement itself.
' True if it is a _region_ flow analysis and the region is defined inside the original expression
Dim origExpressionContainsRegion As Boolean = False
' True if origExpressionContainsRegion is True and region encloses all the initializers
Dim regionEnclosesInitializers As Boolean = False
If Me._firstInRegion IsNot Nothing AndAlso Me._regionPlace = RegionPlace.Before Then
Debug.Assert(Me._lastInRegion IsNot Nothing)
' Check if the region defining node is somewhere inside OriginalExpression
If BoundNodeFinder.ContainsNode(node.OriginalExpression, Me._firstInRegion, _recursionDepth, ConvertInsufficientExecutionStackExceptionToCancelledByStackGuardException()) Then
Debug.Assert(BoundNodeFinder.ContainsNode(node.OriginalExpression, Me._lastInRegion, _recursionDepth, ConvertInsufficientExecutionStackExceptionToCancelledByStackGuardException()))
origExpressionContainsRegion = True
' Does any of initializers contain the node or the node contains initializers?
For Each initializer In node.DraftInitializers
Debug.Assert(initializer.Kind = BoundKind.AssignmentOperator)
Dim initializerExpr As BoundExpression = DirectCast(initializer, BoundAssignmentOperator).Right
If initializerExpr.Kind = BoundKind.LValueToRValueWrapper Then
initializerExpr = DirectCast(initializerExpr, BoundLValueToRValueWrapper).UnderlyingLValue
End If
regionEnclosesInitializers = True
If Me._firstInRegion Is initializerExpr OrElse Not BoundNodeFinder.ContainsNode(Me._firstInRegion, initializerExpr, _recursionDepth, ConvertInsufficientExecutionStackExceptionToCancelledByStackGuardException()) Then
regionEnclosesInitializers = False
Exit For
End If
Next
End If
End If
' NOTE: If origExpressionContainsRegion is True and regionEnclosesInitializers is *False*,
' we should expect that the region is to be properly entered/left while appropriate
' initializer is visited *OR* the node is a literal/local which was not captured,
' but simply reused when needed in With statement body
'
' The assumption above is guarded by the following assert
#If DEBUG Then
If origExpressionContainsRegion AndAlso Not regionEnclosesInitializers Then
Dim containedByInitializer As Boolean = False
For Each initializer In node.DraftInitializers
Debug.Assert(initializer.Kind = BoundKind.AssignmentOperator)
Dim initializerExpr As BoundExpression = DirectCast(initializer, BoundAssignmentOperator).Right
If initializerExpr.Kind = BoundKind.LValueToRValueWrapper Then
initializerExpr = DirectCast(initializerExpr, BoundLValueToRValueWrapper).UnderlyingLValue
End If
containedByInitializer = False
Debug.Assert(initializerExpr IsNot Nothing)
If BoundNodeFinder.ContainsNode(initializerExpr, Me._firstInRegion, _recursionDepth, ConvertInsufficientExecutionStackExceptionToCancelledByStackGuardException()) Then
Debug.Assert(Not containedByInitializer)
containedByInitializer = True
Exit For
End If
Next
Debug.Assert(containedByInitializer OrElse IsNotCapturedExpression(Me._firstInRegion))
End If
#End If
If origExpressionContainsRegion AndAlso regionEnclosesInitializers Then
Me.EnterRegion()
End If
' Visit initializers
For Each initializer In node.DraftInitializers
VisitRvalue(initializer)
Next
If origExpressionContainsRegion Then
If regionEnclosesInitializers Then
Me.LeaveRegion() ' This call also asserts that we are inside the region
Else
' If any of the initializers contained region, we must have exited the region by now or the node is a literal
' which was not captured in initializers, but just reused across when/if needed in With statement body
#If DEBUG Then
Debug.Assert(Me._regionPlace = RegionPlace.After OrElse IsNotCapturedExpression(Me._firstInRegion))
#End If
End If
End If
' Visit body
VisitBlock(node.Body)
If origExpressionContainsRegion AndAlso Me._regionPlace <> RegionPlace.After Then
' The region was a part of the expression, but was not property processed/visited during
' analysis of expression and body; this *may* indicate a bug in flow analysis, otherwise
' it is the case when a struct-typed lvalue expression was never used inside the body AND
' the region was defined by nodes which were not part of initializers; thus, we never
' emitted/visited the node
Debug.Assert(Me._regionPlace = AbstractFlowPass(Of LocalState).RegionPlace.Before)
Me.SetInvalidRegion()
End If
Return Nothing
End Function
#If DEBUG Then
Private Shared Function IsNotCapturedExpression(node As BoundNode) As Boolean
If node Is Nothing Then
Return True
End If
Select Case node.Kind
Case BoundKind.Local
Return DirectCast(node, BoundLocal).Type.IsValueType
Case BoundKind.Parameter
Return DirectCast(node, BoundParameter).Type.IsValueType
Case BoundKind.Literal
Return True
Case BoundKind.MeReference
Return True
Case BoundKind.FieldAccess
Return IsNotCapturedExpression(DirectCast(node, BoundFieldAccess).ReceiverOpt)
Case Else
Return False
End Select
End Function
#End If
Public Overrides Function VisitAnonymousTypeCreationExpression(node As BoundAnonymousTypeCreationExpression) As BoundNode
For Each argument In node.Arguments
VisitRvalue(argument)
Next
Return Nothing
End Function
Public Overrides Function VisitAnonymousTypeFieldInitializer(node As BoundAnonymousTypeFieldInitializer) As BoundNode
VisitRvalue(node.Value)
Return Nothing
End Function
''' <summary>
''' Since each language construct must be handled according to the rules of the language specification,
''' the default visitor reports that the construct for the node is not implemented in the compiler.
''' </summary>
Public Overrides Function DefaultVisit(node As BoundNode) As BoundNode
Return Unimplemented(node, "flow analysis")
End Function
Protected Enum ReadWriteContext
None
CompoundAssignmentTarget
ByRefArgument
End Enum
Protected Overridable Sub VisitLocalInReadWriteContext(node As BoundLocal, rwContext As ReadWriteContext)
End Sub
Protected Overridable Sub VisitFieldAccessInReadWriteContext(node As BoundFieldAccess, rwContext As ReadWriteContext)
VisitFieldAccessInternal(node)
End Sub
Public Overrides Function VisitLambda(node As BoundLambda) As BoundNode
' Control-flow analysis does NOT dive into a lambda, while data-flow analysis does.
Return Nothing
End Function
Public Overrides Function VisitQueryExpression(node As BoundQueryExpression) As BoundNode
' Control-flow analysis does NOT dive into a query expression, while data-flow analysis does.
Return Nothing
End Function
Public Overrides Function VisitLocal(node As BoundLocal) As BoundNode
Return Nothing
End Function
Public Overrides Function VisitRangeVariable(node As BoundRangeVariable) As BoundNode
Return Nothing
End Function
Public Overrides Function VisitLocalDeclaration(node As BoundLocalDeclaration) As BoundNode
If node.InitializerOpt IsNot Nothing Then
VisitRvalue(node.InitializerOpt) ' analyze the expression
End If
Return Nothing
End Function
Private Function IntroduceBlock() As Integer
Dim level = Me._nesting.Count
Me._nesting.Add(0)
Return level
End Function
Private Sub FinalizeBlock(level As Integer)
Me._nesting.RemoveAt(level)
End Sub
Private Sub InitializeBlockStatement(level As Integer, ByRef index As Integer)
Me._nesting(level) = index
index += 1
End Sub
Public Overrides Function VisitBlock(node As BoundBlock) As BoundNode
Dim level = IntroduceBlock()
Dim i As Integer = 0
For Each statement In node.Statements
InitializeBlockStatement(level, i)
VisitStatement(statement)
Next
FinalizeBlock(level)
Return Nothing
End Function
Public Overrides Function VisitExpressionStatement(node As BoundExpressionStatement) As BoundNode
VisitExpressionAsStatement(node.Expression)
Return Nothing
End Function
Private Sub VisitExpressionAsStatement(node As BoundExpression)
VisitRvalue(node)
End Sub
Public Overrides Function VisitLateMemberAccess(node As BoundLateMemberAccess) As BoundNode
' receiver of a latebound access is never modified
VisitRvalue(node.ReceiverOpt)
Return Nothing
End Function
Public Overrides Function VisitLateInvocation(node As BoundLateInvocation) As BoundNode
Dim member = node.Member
Me.Visit(node.Member)
Dim arguments = node.ArgumentsOpt
If Not arguments.IsEmpty Then
Dim isByRef As Boolean
If member.Kind <> BoundKind.LateMemberAccess Then
' this is a Set, Get or Indexing.
isByRef = False
Else
' otherwise assume it is ByRef
isByRef = True
End If
VisitLateBoundArguments(arguments, isByRef)
End If
Return Nothing
End Function
Private Sub VisitLateBoundArguments(arguments As ImmutableArray(Of BoundExpression), isByRef As Boolean)
For Each argument In arguments
VisitLateBoundArgument(argument, isByRef)
Next
If isByRef Then
For Each argument In arguments
WriteArgument(argument, False)
Next
End If
End Sub
Protected Overridable Sub VisitLateBoundArgument(arg As BoundExpression, isByRef As Boolean)
If isByRef Then
VisitLvalue(arg)
Else
VisitRvalue(arg)
End If
End Sub
Public Overrides Function VisitCall(node As BoundCall) As BoundNode
' If the method being called is a partial method without a definition, or is a conditional method
' whose condition is not true at the given syntax location, then the call has no effect and it is ignored for the purposes of
' definite assignment analysis.
Dim callsAreOmitted As Boolean = node.Method.CallsAreOmitted(node.Syntax, node.SyntaxTree)
Dim savedState As LocalState = Nothing
If callsAreOmitted Then
Debug.Assert(Not Me.IsConditionalState)
savedState = Me.State.Clone()
Me.SetUnreachable()
End If
Dim methodGroup As BoundMethodGroup = node.MethodGroupOpt
Dim receiverOpt As BoundExpression = node.ReceiverOpt
Dim method As MethodSymbol = node.Method
' if method group is present, check if we need to enter region
If methodGroup IsNot Nothing AndAlso Me._firstInRegion Is methodGroup AndAlso
Me._regionPlace = RegionPlace.Before Then
Me.EnterRegion()
End If
If receiverOpt IsNot Nothing Then
VisitCallReceiver(receiverOpt, method)
Else
' If receiver is nothing it means that the method being
' accessed is shared; we still want to visit the original receiver
' to handle shared methods called on instance receiver.
Dim originalReceiver As BoundExpression = If(methodGroup IsNot Nothing, methodGroup.ReceiverOpt, Nothing)
If originalReceiver IsNot Nothing AndAlso Not originalReceiver.WasCompilerGenerated Then
Debug.Assert(method.IsShared)
' Do not visit originalReceiver if it is Type/Namespace/TypeOrValueExpression and the method
' is shared, we want region flow analysis to return Succeeded = False in this case
Dim kind As BoundKind = originalReceiver.Kind
If (kind <> BoundKind.TypeExpression) AndAlso
(kind <> BoundKind.NamespaceExpression) AndAlso
(kind <> BoundKind.TypeOrValueExpression) Then
VisitUnreachableReceiver(originalReceiver)
End If
End If
End If
' if method group is present, check if we need to leave region
If methodGroup IsNot Nothing AndAlso Me._lastInRegion Is methodGroup AndAlso IsInside Then
Me.LeaveRegion()
End If
VisitArguments(node.Arguments, method.Parameters)
VisitCallAfterVisitArguments(node)
If callsAreOmitted Then
Me.SetState(savedState)
End If
Return Nothing
End Function
Protected Overridable Sub VisitCallAfterVisitArguments(node As BoundCall)
Dim receiverOpt As BoundExpression = node.ReceiverOpt
Dim method As MethodSymbol = node.Method
If receiverOpt IsNot Nothing AndAlso receiverOpt.IsLValue Then
WriteLValueCallReceiver(receiverOpt, method)
End If
End Sub
Private Sub VisitCallReceiver(receiver As BoundExpression, method As MethodSymbol)
Debug.Assert(receiver IsNot Nothing)
Debug.Assert(method IsNot Nothing)
If Not method.IsReducedExtensionMethod OrElse Not receiver.IsValue() Then
Debug.Assert(method.Kind <> MethodKind.Constructor)
VisitRvalue(receiver)
Else
VisitArgument(receiver, method.CallsiteReducedFromMethod.Parameters(0))
End If
End Sub
Private Sub WriteLValueCallReceiver(receiver As BoundExpression, method As MethodSymbol)
Debug.Assert(receiver IsNot Nothing)
Debug.Assert(receiver.IsLValue)
Debug.Assert(method IsNot Nothing)
If receiver.Type.IsReferenceType Then
' If a receiver is of reference type, it will not be modified if the
' method is not an extension method, note that extension method may
' write to ByRef parameter
Dim reducedFrom As MethodSymbol = method.CallsiteReducedFromMethod
If reducedFrom Is Nothing OrElse reducedFrom.ParameterCount = 0 OrElse Not reducedFrom.Parameters(0).IsByRef Then
Return
End If
End If
WriteArgument(receiver, False)
End Sub
Private Sub VisitArguments(arguments As ImmutableArray(Of BoundExpression), parameters As ImmutableArray(Of ParameterSymbol))
If parameters.IsDefault Then
For Each arg In arguments
VisitRvalue(arg)
Next
Else
Dim n As Integer = Math.Min(parameters.Length, arguments.Length)
' The first loop reflects passing arguments to the method/property
For i = 0 To n - 1
VisitArgument(arguments(i), parameters(i))
Next
' The second loop reflects writing to ByRef arguments after the method returned
For i = 0 To n - 1
If parameters(i).IsByRef Then
WriteArgument(arguments(i), parameters(i).IsOut)
End If
Next
End If
End Sub
Protected Overridable Sub WriteArgument(arg As BoundExpression, isOut As Boolean)
End Sub
Protected Overridable Sub VisitArgument(arg As BoundExpression, p As ParameterSymbol)
If p.IsByRef Then
VisitLvalue(arg)
Else
VisitRvalue(arg)
End If
End Sub
Public Overrides Function VisitDelegateCreationExpression(node As BoundDelegateCreationExpression) As BoundNode
Dim methodGroup As BoundMethodGroup = node.MethodGroupOpt
' if method group is present, check if we need to enter region
If methodGroup IsNot Nothing AndAlso Me._firstInRegion Is methodGroup AndAlso
Me._regionPlace = AbstractFlowPass(Of LocalState).RegionPlace.Before Then
Me.EnterRegion()
End If
Dim receiverOpt As BoundExpression = node.ReceiverOpt
Dim method As MethodSymbol = node.Method
If receiverOpt IsNot Nothing Then
If Not method.IsReducedExtensionMethod OrElse Not receiverOpt.IsValue() Then
Debug.Assert(method.Kind <> MethodKind.Constructor)
VisitRvalue(receiverOpt)
Else
VisitArgument(receiverOpt, method.CallsiteReducedFromMethod.Parameters(0))
End If
Else
' If receiver is nothing it means that the method being
' accessed is shared; we still want to visit the original receiver
' to handle shared methods called on instance receiver.
Dim originalReceiver As BoundExpression = If(methodGroup IsNot Nothing, methodGroup.ReceiverOpt, Nothing)
If originalReceiver IsNot Nothing AndAlso Not originalReceiver.WasCompilerGenerated Then
Debug.Assert(method.IsShared)
' Do not visit originalReceiver if it is Type/Namespace/TypeOrValueExpression and the method
' is shared, we want region flow analysis to return Succeeded = False in this case
Dim kind As BoundKind = originalReceiver.Kind
If (kind <> BoundKind.TypeExpression) AndAlso
(kind <> BoundKind.NamespaceExpression) AndAlso
(kind <> BoundKind.TypeOrValueExpression) Then
VisitUnreachableReceiver(originalReceiver)
End If
End If
End If
' if method group is present, check if we need to leave region
If methodGroup IsNot Nothing AndAlso Me._lastInRegion Is methodGroup AndAlso IsInside Then
Me.LeaveRegion()
End If
Return Nothing
End Function
Public Overrides Function VisitBadExpression(node As BoundBadExpression) As BoundNode
' Visit the child nodes in bad expressions so that uses of locals in bad expressions are recorded. This
' suppresses warnings about unused locals.
For Each child In node.ChildBoundNodes
VisitRvalue(TryCast(child, BoundExpression))
Next
Return Nothing
End Function
Public Overrides Function VisitBadStatement(node As BoundBadStatement) As BoundNode
' Visit the child nodes of the bad statement
For Each child In node.ChildBoundNodes
Dim statement = TryCast(child, BoundStatement)
If statement IsNot Nothing Then
VisitStatement(TryCast(child, BoundStatement))
Else
Dim expression = TryCast(child, BoundExpression)
If expression IsNot Nothing Then
VisitExpressionAsStatement(expression)
Else
Visit(child)
End If
End If
Next
Return Nothing
End Function
Public Overrides Function VisitBadVariable(node As BoundBadVariable) As BoundNode
If node.IsLValue Then
VisitLvalue(node.Expression)
Else
VisitRvalue(node.Expression)
End If
Return Nothing
End Function
Public Overrides Function VisitTypeExpression(node As BoundTypeExpression) As BoundNode
' Visit the receiver even though the expression value
' is ignored since the region may be within the receiver.
VisitUnreachableReceiver(node.UnevaluatedReceiverOpt)
Return Nothing
End Function
Public Overrides Function VisitLiteral(node As BoundLiteral) As BoundNode
Return Nothing
End Function
Public Overrides Function VisitConversion(node As BoundConversion) As BoundNode
VisitRvalue(node.Operand)
Return Nothing
End Function
Public Overrides Function VisitRelaxationLambda(node As BoundRelaxationLambda) As BoundNode
Throw ExceptionUtilities.Unreachable
End Function
Public Overrides Function VisitConvertedTupleElements(node As BoundConvertedTupleElements) As BoundNode
Throw ExceptionUtilities.Unreachable
End Function
Public Overrides Function VisitUserDefinedConversion(node As BoundUserDefinedConversion) As BoundNode
VisitRvalue(node.UnderlyingExpression)
Return Nothing
End Function
Public Overrides Function VisitIfStatement(node As BoundIfStatement) As BoundNode
VisitCondition(node.Condition)
Dim trueState As LocalState = Me.StateWhenTrue
Dim falseState As LocalState = Me.StateWhenFalse
Me.SetState(trueState)
VisitStatement(node.Consequence)
trueState = Me.State
Me.SetState(falseState)
If node.AlternativeOpt IsNot Nothing Then
VisitStatement(node.AlternativeOpt)
End If
Me.IntersectWith(Me.State, trueState)
Return Nothing
End Function
Public Overrides Function VisitTernaryConditionalExpression(node As BoundTernaryConditionalExpression) As BoundNode
VisitCondition(node.Condition)
Dim trueState As LocalState = Me.StateWhenTrue
Dim falseState As LocalState = Me.StateWhenFalse
If IsConstantTrue(node.Condition) Then
Me.SetState(falseState)
VisitRvalue(node.WhenFalse)
Me.SetState(trueState)
VisitRvalue(node.WhenTrue)
ElseIf IsConstantFalse(node.Condition) Then
Me.SetState(trueState)
VisitRvalue(node.WhenTrue)
Me.SetState(falseState)
VisitRvalue(node.WhenFalse)
Else
Me.SetState(trueState)
VisitRvalue(node.WhenTrue)
Me.Unsplit()
trueState = Me.State
Me.SetState(falseState)
VisitRvalue(node.WhenFalse)
Me.Unsplit()
Me.IntersectWith(Me.State, trueState)
End If
Return Nothing
End Function
Public Overrides Function VisitBinaryConditionalExpression(node As BoundBinaryConditionalExpression) As BoundNode
VisitRvalue(node.TestExpression)
If node.TestExpression.IsConstant AndAlso node.TestExpression.ConstantValueOpt.IsNothing Then
' this may be something like 'If(CType(Nothing, String), AnyExpression)'
VisitRvalue(node.ElseExpression)
Else
' all other cases including 'If("const", AnyExpression)'
Dim savedState As LocalState = Me.State.Clone()
VisitRvalue(node.ElseExpression)
Me.SetState(savedState)
End If
Return Nothing
End Function
Public Overrides Function VisitConditionalAccess(node As BoundConditionalAccess) As BoundNode
VisitRvalue(node.Receiver)
If node.Receiver.IsConstant Then
If node.Receiver.ConstantValueOpt.IsNothing Then
Dim savedState As LocalState = Me.State.Clone()
SetUnreachable()
VisitRvalue(node.AccessExpression)
Me.SetState(savedState)
Else
VisitRvalue(node.AccessExpression)
End If
Else
Dim savedState As LocalState = Me.State.Clone()
VisitRvalue(node.AccessExpression)
IntersectWith(Me.State, savedState)
End If
Return Nothing
End Function
Public Overrides Function VisitLoweredConditionalAccess(node As BoundLoweredConditionalAccess) As BoundNode
VisitRvalue(node.ReceiverOrCondition)
Dim savedState As LocalState = Me.State.Clone()
VisitRvalue(node.WhenNotNull)
IntersectWith(Me.State, savedState)
If node.WhenNullOpt IsNot Nothing Then
savedState = Me.State.Clone()
VisitRvalue(node.WhenNullOpt)
IntersectWith(Me.State, savedState)
End If
Return Nothing
End Function
Public Overrides Function VisitComplexConditionalAccessReceiver(node As BoundComplexConditionalAccessReceiver) As BoundNode
Dim savedState As LocalState = Me.State.Clone()
VisitLvalue(node.ValueTypeReceiver)
IntersectWith(Me.State, savedState)
savedState = Me.State.Clone()
VisitRvalue(node.ReferenceTypeReceiver)
IntersectWith(Me.State, savedState)
Return Nothing
End Function
Public Overrides Function VisitConditionalAccessReceiverPlaceholder(node As BoundConditionalAccessReceiverPlaceholder) As BoundNode
Return Nothing
End Function
Public Overrides Function VisitReturnStatement(node As BoundReturnStatement) As BoundNode
' Set unreachable and pending branch for all returns except for the final return that is auto generated
If Not node.IsEndOfMethodReturn Then
VisitRvalue(node.ExpressionOpt)
Me._pendingBranches.Add(New PendingBranch(node, Me.State, Me._nesting))
SetUnreachable()
End If
Return Nothing
End Function
Public Overrides Function VisitYieldStatement(node As BoundYieldStatement) As BoundNode
VisitRvalue(node.Expression)
Me._pendingBranches.Add(New PendingBranch(node, Me.State, Me._nesting))
Return Nothing
End Function
Public Overrides Function VisitStopStatement(node As BoundStopStatement) As BoundNode
Return Nothing
End Function
Public Overrides Function VisitEndStatement(node As BoundEndStatement) As BoundNode
SetUnreachable()
Return Nothing
End Function
Public Overrides Function VisitMeReference(node As BoundMeReference) As BoundNode
Return Nothing
End Function
Public Overrides Function VisitParameter(node As BoundParameter) As BoundNode
Return Nothing
End Function
Public Overrides Function VisitObjectCreationExpression(node As BoundObjectCreationExpression) As BoundNode
For Each e In node.Arguments
VisitRvalue(e)
Next
VisitObjectCreationExpressionInitializer(node.InitializerOpt)
Return Nothing
End Function
Public Overrides Function VisitNoPiaObjectCreationExpression(node As BoundNoPiaObjectCreationExpression) As BoundNode
VisitObjectCreationExpressionInitializer(node.InitializerOpt)
Return Nothing
End Function
Protected Overridable Sub VisitObjectCreationExpressionInitializer(node As BoundObjectInitializerExpressionBase)
Visit(node)
End Sub
Private Function VisitObjectInitializerExpressionBase(node As BoundObjectInitializerExpressionBase) As BoundNode
For Each initializer In node.Initializers
VisitExpressionAsStatement(initializer)
Next
Return Nothing
End Function
Public Overrides Function VisitCollectionInitializerExpression(node As BoundCollectionInitializerExpression) As BoundNode
Return Me.VisitObjectInitializerExpressionBase(node)
End Function
Public Overrides Function VisitObjectInitializerExpression(node As BoundObjectInitializerExpression) As BoundNode
Return Me.VisitObjectInitializerExpressionBase(node)
End Function
Public Overrides Function VisitNewT(node As BoundNewT) As BoundNode
Visit(node.InitializerOpt)
Return Nothing
End Function
Public Overrides Function VisitRedimStatement(node As BoundRedimStatement) As BoundNode
For Each clause In node.Clauses
Visit(clause)
Next
Return Nothing
End Function
Public Overrides Function VisitEraseStatement(node As BoundEraseStatement) As BoundNode
For Each clause In node.Clauses
Visit(clause)
Next
Return Nothing
End Function
Protected Overridable ReadOnly Property SuppressRedimOperandRvalueOnPreserve As Boolean
Get
Return False
End Get
End Property
Public Overrides Function VisitRedimClause(node As BoundRedimClause) As BoundNode
If node.Preserve AndAlso Not SuppressRedimOperandRvalueOnPreserve Then
VisitRvalue(node.Operand)
End If
VisitLvalue(node.Operand)
For Each index In node.Indices
VisitRvalue(index)
Next
Return Nothing
End Function
Public Overrides Function VisitAssignmentOperator(node As BoundAssignmentOperator) As BoundNode
If node.LeftOnTheRightOpt Is Nothing Then
VisitLvalue(node.Left)
Else
SetPlaceholderSubstitute(node.LeftOnTheRightOpt, node.Left)
End If
VisitRvalue(node.Right)
If node.LeftOnTheRightOpt IsNot Nothing Then
RemovePlaceholderSubstitute(node.LeftOnTheRightOpt)
End If
Return Nothing
End Function
Public Overrides Function VisitMidResult(node As BoundMidResult) As BoundNode
VisitRvalue(node.Original)
VisitRvalue(node.Start)
If node.LengthOpt IsNot Nothing Then
VisitRvalue(node.LengthOpt)
End If
VisitRvalue(node.Source)
Return Nothing
End Function
Public Overrides Function VisitReferenceAssignment(node As BoundReferenceAssignment) As BoundNode
VisitRvalue(node.ByRefLocal)
VisitRvalue(node.LValue)
Return Nothing
End Function
Public Overrides Function VisitCompoundAssignmentTargetPlaceholder(node As BoundCompoundAssignmentTargetPlaceholder) As BoundNode
Dim replacement As BoundExpression = GetPlaceholderSubstitute(node)
If replacement IsNot Nothing Then
VisitRvalue(replacement, ReadWriteContext.CompoundAssignmentTarget)
End If
Return Nothing
End Function
Public Overrides Function VisitFieldAccess(node As BoundFieldAccess) As BoundNode
VisitFieldAccessInternal(node)
Return Nothing
End Function
Private Function VisitFieldAccessInternal(node As BoundFieldAccess) As BoundNode
Dim receiverOpt = node.ReceiverOpt
If FieldAccessMayRequireTracking(node) Then
VisitLvalue(receiverOpt)
ElseIf node.FieldSymbol.IsShared Then
VisitUnreachableReceiver(receiverOpt)
Else
VisitRvalue(receiverOpt)
End If
Return Nothing
End Function
''' <summary> Bound field access passed may require tracking if it is an access to a non-shared structure field </summary>
Protected Shared Function FieldAccessMayRequireTracking(fieldAccess As BoundFieldAccess) As Boolean
If fieldAccess.FieldSymbol.IsShared Then
Return False
End If
Dim receiver = fieldAccess.ReceiverOpt
' Receiver must exist
If receiver Is Nothing Then
Return False
End If
' Receiver is of non-primitive structure type
Dim receiverType = receiver.Type
Return IsNonPrimitiveValueType(receiverType)
End Function
Public Overrides Function VisitPropertyAccess(node As BoundPropertyAccess) As BoundNode
Dim propertyGroup As BoundPropertyGroup = node.PropertyGroupOpt
' if property group is present, check if we need to enter region
If propertyGroup IsNot Nothing AndAlso Me._firstInRegion Is propertyGroup AndAlso
Me._regionPlace = RegionPlace.Before Then
Me.EnterRegion()
End If
If node.ReceiverOpt IsNot Nothing Then
VisitRvalue(node.ReceiverOpt)
Else
' If receiver is nothing it means that the property being
' accessed is shared; we still want to visit the original receiver
' to handle shared properties accessed on instance receiver.
Dim originalReceiver As BoundExpression = If(propertyGroup IsNot Nothing, propertyGroup.ReceiverOpt, Nothing)
If originalReceiver IsNot Nothing AndAlso Not originalReceiver.WasCompilerGenerated Then
Debug.Assert(node.PropertySymbol.IsShared)
' Do not visit originalReceiver if it is Type/Namespace/TypeOrValueExpression and the property
' is shared, we want region flow analysis to return Succeeded = False in this case
Dim kind As BoundKind = originalReceiver.Kind
If (kind <> BoundKind.TypeExpression) AndAlso
(kind <> BoundKind.NamespaceExpression) AndAlso
(kind <> BoundKind.TypeOrValueExpression) Then
VisitUnreachableReceiver(originalReceiver)
End If
End If
End If
' if property group is present, check if we need to leave region
If propertyGroup IsNot Nothing AndAlso Me._lastInRegion Is propertyGroup AndAlso IsInside Then
Me.LeaveRegion()
End If
For Each argument In node.Arguments
VisitRvalue(argument)
Next
Return Nothing
End Function
''' <summary>
''' If a receiver is included in cases where the receiver will not be
''' evaluated (an instance for a shared method for instance), we
''' still want to visit the receiver but treat it as unreachable code.
''' </summary>
Private Sub VisitUnreachableReceiver(receiver As BoundExpression)
Debug.Assert(Not Me.IsConditionalState)
If receiver IsNot Nothing Then
Dim saved As LocalState = Me.State.Clone()
Me.SetUnreachable()
Me.VisitRvalue(receiver)
Me.SetState(saved)
End If
End Sub
Public Overrides Function VisitAsNewLocalDeclarations(node As BoundAsNewLocalDeclarations) As BoundNode
VisitRvalue(node.Initializer)
For Each v In node.LocalDeclarations
Visit(v)
Next
Return Nothing
End Function
Public Overrides Function VisitDimStatement(node As BoundDimStatement) As BoundNode
For Each v In node.LocalDeclarations
VisitStatement(v)
Next
Return Nothing
End Function
Public Overrides Function VisitWhileStatement(node As BoundWhileStatement) As BoundNode
' while node.Condition node.Body node.ContinueLabel: node.BreakLabel:
LoopHead(node)
VisitCondition(node.Condition)
Dim bodyState As LocalState = Me.StateWhenTrue
Dim breakState As LocalState = Me.StateWhenFalse
Me.SetState(bodyState)
VisitStatement(node.Body)
ResolveContinues(node.ContinueLabel)
LoopTail(node)
ResolveBreaks(breakState, node.ExitLabel)
Return Nothing
End Function
Public Overrides Function VisitSelectStatement(node As BoundSelectStatement) As BoundNode
VisitStatement(node.ExpressionStatement)
Dim caseBlocks = node.CaseBlocks
If caseBlocks.Any() Then
VisitCaseBlocks(caseBlocks)
ResolveBreaks(Me.State, node.ExitLabel)
End If
Return Nothing
End Function
Private Sub VisitCaseBlocks(caseBlocks As ImmutableArray(Of BoundCaseBlock))
Debug.Assert(caseBlocks.Any())
Dim caseBlockStateBuilder = ArrayBuilder(Of LocalState).GetInstance(caseBlocks.Length)
Dim hasCaseElse = False
Dim curIndex As Integer = 0
Dim lastIndex As Integer = caseBlocks.Length - 1
For Each caseBlock In caseBlocks
' Visit case statement
VisitStatement(caseBlock.CaseStatement)
' Case statement might have a non-null conditionOpt for the condition expression.
' However, conditionOpt cannot be a compile time constant expression.
' VisitCaseStatement must have unsplit the states into a non-conditional state for this scenario.
Debug.Assert(Not Me.IsConditionalState)
' save the current state for next case block.
Dim savedState As LocalState = Me.State.Clone()
' Visit case block body
VisitStatement(caseBlock.Body)
hasCaseElse = hasCaseElse OrElse caseBlock.Syntax.Kind = SyntaxKind.CaseElseBlock
' If the select statement had a case else block, then the state at the end
' of select statement is the merge of states at the end of all case blocks.
' Otherwise, the end state is merge of states at the end of all case blocks
' and saved state prior to visiting the last case block body (i.e. no matching case state)
If curIndex <> lastIndex OrElse Not hasCaseElse Then
caseBlockStateBuilder.Add(Me.State.Clone())
Me.SetState(savedState)
End If
curIndex = curIndex + 1
Next
For Each localState In caseBlockStateBuilder
Me.IntersectWith(Me.State, localState)
Next
caseBlockStateBuilder.Free()
End Sub
Public Overrides Function VisitCaseBlock(node As BoundCaseBlock) As BoundNode
Throw ExceptionUtilities.Unreachable
End Function
Public Overrides Function VisitCaseStatement(node As BoundCaseStatement) As BoundNode
If node.ConditionOpt IsNot Nothing Then
' Case clause expressions cannot be constant expression.
Debug.Assert(node.ConditionOpt.ConstantValueOpt Is Nothing)
VisitRvalue(node.ConditionOpt)
Else
For Each clause In node.CaseClauses
Select Case clause.Kind
Case BoundKind.RelationalCaseClause
VisitRelationalCaseClause(DirectCast(clause, BoundRelationalCaseClause))
Case BoundKind.SimpleCaseClause
VisitSimpleCaseClause(DirectCast(clause, BoundSimpleCaseClause))
Case BoundKind.RangeCaseClause
VisitRangeCaseClause(DirectCast(clause, BoundRangeCaseClause))
Case Else
Throw ExceptionUtilities.UnexpectedValue(clause.Kind)
End Select
Next
End If
Return Nothing
End Function
Public Overrides Function VisitRelationalCaseClause(node As BoundRelationalCaseClause) As BoundNode
' Exactly one of the operand or condition must be non-null
Debug.Assert(node.ValueOpt IsNot Nothing Xor node.ConditionOpt IsNot Nothing)
If node.ValueOpt IsNot Nothing Then
VisitRvalue(node.ValueOpt)
Else
VisitRvalue(node.ConditionOpt)
End If
Return Nothing
End Function
Public Overrides Function VisitSimpleCaseClause(node As BoundSimpleCaseClause) As BoundNode
' Exactly one of the value or condition must be non-null
Debug.Assert(node.ValueOpt IsNot Nothing Xor node.ConditionOpt IsNot Nothing)
If node.ValueOpt IsNot Nothing Then
VisitRvalue(node.ValueOpt)
Else
VisitRvalue(node.ConditionOpt)
End If
Return Nothing
End Function
Public Overrides Function VisitRangeCaseClause(node As BoundRangeCaseClause) As BoundNode
' Exactly one of the LowerBoundOpt or LowerBoundConditionOpt must be non-null
Debug.Assert(node.LowerBoundOpt IsNot Nothing Xor node.LowerBoundConditionOpt IsNot Nothing)
If node.LowerBoundOpt IsNot Nothing Then
VisitRvalue(node.LowerBoundOpt)
Else
VisitRvalue(node.LowerBoundConditionOpt)
End If
' Exactly one of the UpperBoundOpt or UpperBoundConditionOpt must be non-null
Debug.Assert(node.UpperBoundOpt IsNot Nothing Xor node.UpperBoundConditionOpt IsNot Nothing)
If node.UpperBoundOpt IsNot Nothing Then
VisitRvalue(node.UpperBoundOpt)
Else
VisitRvalue(node.UpperBoundConditionOpt)
End If
Return Nothing
End Function
Protected Overridable Sub VisitForControlInitialization(node As BoundForToStatement)
VisitLvalue(node.ControlVariable)
End Sub
Protected Overridable Sub VisitForControlInitialization(node As BoundForEachStatement)
VisitLvalue(node.ControlVariable)
End Sub
Protected Overridable Sub VisitForInitValues(node As BoundForToStatement)
VisitRvalue(node.InitialValue)
VisitRvalue(node.LimitValue)
VisitRvalue(node.StepValue)
End Sub
Protected Overridable Sub VisitForStatementVariableDeclaration(node As BoundForStatement)
End Sub
Public Overrides Function VisitForEachStatement(node As BoundForEachStatement) As BoundNode
' This is a bit subtle.
' initialization of For each control variable happens after the collection is evaluated.
' For example the following statement should give a warning:
' For each i As Object in Me.GetSomeCollection(i) ' <- use of uninitialized i
VisitForStatementVariableDeclaration(node)
VisitRvalue(node.Collection)
LoopHead(node)
Me.Split()
Dim bodyState As LocalState = Me.StateWhenTrue
Dim breakState As LocalState = Me.StateWhenFalse
Me.SetState(bodyState)
' The control Variable is only considered initialized if the body was entered.
VisitForControlInitialization(node)
VisitStatement(node.Body)
ResolveContinues(node.ContinueLabel)
LoopTail(node)
ResolveBreaks(breakState, node.ExitLabel)
Return Nothing
End Function
Public Overrides Function VisitUsingStatement(node As BoundUsingStatement) As BoundNode
Debug.Assert(Not node.ResourceList.IsDefault OrElse node.ResourceExpressionOpt IsNot Nothing)
' A using statement can come in two flavors: using <expression> and using <variable declarations>
' visit possible resource expression
If node.ResourceExpressionOpt IsNot Nothing Then
VisitRvalue(node.ResourceExpressionOpt)
Else
' visit all declarations
For Each variableDeclaration In node.ResourceList
Visit(variableDeclaration)
Next
End If
VisitStatement(node.Body)
Return Nothing
End Function
Public Overrides Function VisitForToStatement(node As BoundForToStatement) As BoundNode
' This is a bit subtle.
' initialization of For control variable happens after initial value, limit and step are evaluated.
' For example the following statement should give a warning:
' For i As Object = 0 To i ' <- use of uninitialized i
VisitForStatementVariableDeclaration(node)
VisitForInitValues(node)
VisitForControlInitialization(node)
LoopHead(node)
Me.Split()
Dim bodyState As LocalState = Me.StateWhenTrue
Dim breakState As LocalState = Me.StateWhenFalse
Me.SetState(bodyState)
VisitStatement(node.Body)
ResolveContinues(node.ContinueLabel)
LoopTail(node)
ResolveBreaks(breakState, node.ExitLabel)
Return Nothing
End Function
Public Overrides Function VisitTryStatement(node As BoundTryStatement) As BoundNode
Dim oldPending = SavePending()
Dim level = IntroduceBlock()
Dim i As Integer = 0
Dim initialState = Me.State.Clone()
InitializeBlockStatement(level, i)
VisitTryBlock(node.TryBlock, node, initialState)
Dim finallyState = initialState.Clone()
Dim endState = Me.State
For Each catchBlock In node.CatchBlocks
Me.SetState(initialState.Clone())
InitializeBlockStatement(level, i)
VisitCatchBlock(catchBlock, finallyState)
IntersectWith(endState, Me.State)
Next
If node.FinallyBlockOpt IsNot Nothing Then
Dim tryAndCatchPending As SavedPending = SavePending()
Me.SetState(finallyState)
Dim unsetInFinally = AllBitsSet()
InitializeBlockStatement(level, i)
VisitFinallyBlock(node.FinallyBlockOpt, unsetInFinally)
For Each pend In tryAndCatchPending.PendingBranches
' Do not union if branch is a Yield statement
Dim unionBranchWithFinallyState As Boolean = pend.Branch.Kind <> BoundKind.YieldStatement
' or if the branch goes from Catch to Try block
If unionBranchWithFinallyState Then
If BothBranchAndLabelArePrefixedByNesting(pend, ignoreLast:=True) Then
unionBranchWithFinallyState = False
End If
End If
If unionBranchWithFinallyState Then
Me.UnionWith(pend.State, Me.State)
If Me.TrackUnassignments Then
Me.IntersectWith(pend.State, unsetInFinally)
End If
End If
Next
RestorePending(tryAndCatchPending)
Me.UnionWith(endState, Me.State)
If Me.TrackUnassignments Then
Me.IntersectWith(endState, unsetInFinally)
End If
End If
Me.SetState(endState)
FinalizeBlock(level)
If node.ExitLabelOpt IsNot Nothing Then
ResolveBreaks(endState, node.ExitLabelOpt)
End If
RestorePending(oldPending)
Return Nothing
End Function
Protected Overridable Sub VisitTryBlock(tryBlock As BoundStatement, node As BoundTryStatement, ByRef tryState As LocalState)
VisitStatement(tryBlock)
End Sub
Protected Overridable Overloads Sub VisitCatchBlock(catchBlock As BoundCatchBlock, ByRef finallyState As LocalState)
If catchBlock.ExceptionSourceOpt IsNot Nothing Then
VisitLvalue(catchBlock.ExceptionSourceOpt)
End If
If catchBlock.ErrorLineNumberOpt IsNot Nothing Then
VisitRvalue(catchBlock.ErrorLineNumberOpt)
End If
If catchBlock.ExceptionFilterOpt IsNot Nothing Then
VisitRvalue(catchBlock.ExceptionFilterOpt)
End If
VisitBlock(catchBlock.Body)
End Sub
Protected Overridable Sub VisitFinallyBlock(finallyBlock As BoundStatement, ByRef unsetInFinally As LocalState)
VisitStatement(finallyBlock)
End Sub
Public Overrides Function VisitArrayAccess(node As BoundArrayAccess) As BoundNode
VisitRvalue(node.Expression)
For Each i In node.Indices
VisitRvalue(i)
Next
Return Nothing
End Function
Public NotOverridable Overrides Function VisitBinaryOperator(node As BoundBinaryOperator) As BoundNode
' Do not blow the stack due to a deep recursion on the left.
Dim stack = ArrayBuilder(Of BoundBinaryOperator).GetInstance()
Dim binary As BoundBinaryOperator = node
Dim child As BoundExpression = node.Left
Do
If child.Kind <> BoundKind.BinaryOperator Then
Exit Do
End If
stack.Push(binary)
binary = DirectCast(child, BoundBinaryOperator)
child = binary.Left
Loop
' As we emulate visiting, enter the region if needed.
' We shouldn't do this for the top most node,
' VisitBinaryOperator caller such as VisitRvalue(...) does this.
If Me._regionPlace = RegionPlace.Before Then
If stack.Count > 0 Then
' Skipping the first node
Debug.Assert(stack(0) Is node)
For i As Integer = 1 To stack.Count - 1
If stack(i) Is Me._firstInRegion Then
Me.EnterRegion()
GoTo EnteredRegion
End If
Next
' Note, the last binary operator is not pushed to the stack, it is stored in [binary].
Debug.Assert(binary IsNot node)
If binary Is Me._firstInRegion Then
Me.EnterRegion()
End If
EnteredRegion:
Else
Debug.Assert(binary Is node)
End If
End If
Select Case binary.OperatorKind And BinaryOperatorKind.OpMask
Case BinaryOperatorKind.AndAlso,
BinaryOperatorKind.OrElse
VisitCondition(child)
Case Else
VisitRvalue(child)
End Select
Do
Select Case binary.OperatorKind And BinaryOperatorKind.OpMask
Case BinaryOperatorKind.AndAlso
Dim leftTrue As LocalState = Me.StateWhenTrue
Dim leftFalse As LocalState = Me.StateWhenFalse
Me.SetState(leftTrue)
VisitCondition(binary.Right)
Dim resultTrue As LocalState = Me.StateWhenTrue
Dim resultFalse As LocalState = leftFalse
IntersectWith(resultFalse, Me.StateWhenFalse)
Me.SetConditionalState(resultTrue, resultFalse)
Exit Select
Case BinaryOperatorKind.OrElse
Dim leftTrue As LocalState = Me.StateWhenTrue
Dim leftFalse As LocalState = Me.StateWhenFalse
Me.SetState(leftFalse)
VisitCondition(binary.Right)
Dim resultTrue As LocalState = Me.StateWhenTrue
IntersectWith(resultTrue, leftTrue)
Dim resultFalse As LocalState = Me.StateWhenFalse
Me.SetConditionalState(resultTrue, resultFalse)
Exit Select
Case Else
VisitRvalue(binary.Right)
End Select
If stack.Count > 0 Then
child = binary
binary = stack.Pop()
' Do things that VisitCondition/VisitRvalue would have done for us if we were to call them
' for the child
Select Case binary.OperatorKind And BinaryOperatorKind.OpMask
Case BinaryOperatorKind.AndAlso,
BinaryOperatorKind.OrElse
' Do things that VisitCondition would have done for us if we were to call it
' for the child
AdjustConditionalState(child) ' VisitCondition does this
Case Else
Me.Unsplit() ' VisitRvalue does this
End Select
' VisitCondition/VisitRvalue do this
If child Is Me._lastInRegion AndAlso IsInside Then
Me.LeaveRegion()
End If
Else
Exit Do
End If
Loop
Debug.Assert(binary Is node)
stack.Free()
Return Nothing
End Function
Public Overrides Function VisitUserDefinedBinaryOperator(node As BoundUserDefinedBinaryOperator) As BoundNode
VisitRvalue(node.UnderlyingExpression)
Return Nothing
End Function
Public Overrides Function VisitUserDefinedShortCircuitingOperator(node As BoundUserDefinedShortCircuitingOperator) As BoundNode
If node.LeftOperand IsNot Nothing Then
VisitRvalue(node.LeftOperand)
End If
VisitRvalue(node.BitwiseOperator)
Return Nothing
End Function
Public Overrides Function VisitAddHandlerStatement(node As BoundAddHandlerStatement) As BoundNode
Return VisitAddRemoveHandlerStatement(node)
End Function
Public Overrides Function VisitRemoveHandlerStatement(node As BoundRemoveHandlerStatement) As BoundNode
Return VisitAddRemoveHandlerStatement(node)
End Function
Public Overrides Function VisitAddressOfOperator(node As BoundAddressOfOperator) As BoundNode
Visit(node.MethodGroup)
Return Nothing
End Function
Public Overrides Function VisitLateAddressOfOperator(node As BoundLateAddressOfOperator) As BoundNode
Visit(node.MemberAccess)
Return Nothing
End Function
Private Function VisitAddRemoveHandlerStatement(node As BoundAddRemoveHandlerStatement) As BoundNode
' from the data/control flow perspective AddRemoveHandler
' statement is just a trivial binary operator.
VisitRvalue(node.EventAccess)
VisitRvalue(node.Handler)
Return Nothing
End Function
Public Overrides Function VisitEventAccess(node As BoundEventAccess) As BoundNode
Dim receiver = node.ReceiverOpt
If receiver IsNot Nothing Then
VisitRvalue(node.ReceiverOpt)
End If
Return Nothing
End Function
Public Overrides Function VisitRaiseEventStatement(node As BoundRaiseEventStatement) As BoundNode
Me.VisitExpressionAsStatement(node.EventInvocation)
Return Nothing
End Function
Public Overrides Function VisitParenthesized(node As BoundParenthesized) As BoundNode
VisitRvalue(node.Expression)
Return Nothing
End Function
Public Overrides Function VisitUnaryOperator(node As BoundUnaryOperator) As BoundNode
If node.OperatorKind = UnaryOperatorKind.Not Then
VisitCondition(node.Operand)
Me.SetConditionalState(Me.StateWhenFalse, Me.StateWhenTrue)
Else
VisitRvalue(node.Operand)
End If
Return Nothing
End Function
Public Overrides Function VisitUserDefinedUnaryOperator(node As BoundUserDefinedUnaryOperator) As BoundNode
VisitRvalue(node.UnderlyingExpression)
Return Nothing
End Function
Public Overrides Function VisitNullableIsTrueOperator(node As BoundNullableIsTrueOperator) As BoundNode
VisitRvalue(node.Operand)
Return Nothing
End Function
Public Overrides Function VisitArrayCreation(node As BoundArrayCreation) As BoundNode
For Each e1 In node.Bounds
VisitRvalue(e1)
Next
If node.InitializerOpt IsNot Nothing AndAlso Not node.InitializerOpt.Initializers.IsDefault Then
For Each element In node.InitializerOpt.Initializers
VisitRvalue(element)
Next
End If
Return Nothing
End Function
Public Overrides Function VisitArrayInitialization(node As BoundArrayInitialization) As BoundNode
For Each initializer In node.Initializers
VisitRvalue(initializer)
Next
Return Nothing
End Function
Public Overrides Function VisitArrayLiteral(node As BoundArrayLiteral) As BoundNode
For Each arrayBound In node.Bounds
VisitRvalue(arrayBound)
Next
VisitRvalue(node.Initializer)
Return Nothing
End Function
Public Overrides Function VisitTupleLiteral(node As BoundTupleLiteral) As BoundNode
Return VisitTupleExpression(node)
End Function
Public Overrides Function VisitConvertedTupleLiteral(node As BoundConvertedTupleLiteral) As BoundNode
Return VisitTupleExpression(node)
End Function
Private Function VisitTupleExpression(node As BoundTupleExpression) As BoundNode
For Each argument In node.Arguments
VisitRvalue(argument)
Next
Return Nothing
End Function
Public Overrides Function VisitDirectCast(node As BoundDirectCast) As BoundNode
VisitRvalue(node.Operand)
Return Nothing
End Function
Public Overrides Function VisitTryCast(node As BoundTryCast) As BoundNode
VisitRvalue(node.Operand)
Return Nothing
End Function
Public Overrides Function VisitTypeOf(node As BoundTypeOf) As BoundNode
VisitRvalue(node.Operand)
Return Nothing
End Function
Public Overrides Function VisitMethodGroup(node As BoundMethodGroup) As BoundNode
VisitRvalue(node.ReceiverOpt)
Return Nothing
End Function
Public Overrides Function VisitPropertyGroup(node As BoundPropertyGroup) As BoundNode
VisitRvalue(node.ReceiverOpt)
Return Nothing
End Function
Public Overrides Function VisitGetType(node As BoundGetType) As BoundNode
VisitTypeExpression(node.SourceType)
Return Nothing
End Function
Public Overrides Function VisitSequencePoint(node As BoundSequencePoint) As BoundNode
VisitStatement(node.StatementOpt)
Return Nothing
End Function
Public Overrides Function VisitSequencePointWithSpan(node As BoundSequencePointWithSpan) As BoundNode
VisitStatement(node.StatementOpt)
Return Nothing
End Function
Public Overrides Function VisitStatementList(node As BoundStatementList) As BoundNode
For Each statement In node.Statements
Visit(statement)
Next
Return Nothing
End Function
Public Overrides Function VisitSyncLockStatement(node As BoundSyncLockStatement) As BoundNode
' visit SyncLock expression
VisitRvalue(node.LockExpression)
' visit body
VisitStatement(node.Body)
Return Nothing
End Function
Public Overrides Function VisitUnboundLambda(node As UnboundLambda) As BoundNode
Dim boundLambda As BoundLambda = node.BindForErrorRecovery()
Debug.Assert(boundLambda IsNot Nothing)
' NOTE: we can skip checking if Me._firstInRegion is nothing because 'boundLambda' is not nothing
If boundLambda Is Me._firstInRegion AndAlso Me._regionPlace = RegionPlace.Before Then
Me.EnterRegion()
End If
VisitLambda(node.BindForErrorRecovery())
' NOTE: we can skip checking if Me._firstInRegion is nothing because 'boundLambda' is not nothing
If boundLambda Is Me._lastInRegion AndAlso IsInside Then
Me.LeaveRegion()
End If
Return Nothing
End Function
Public Overrides Function VisitExitStatement(node As BoundExitStatement) As BoundNode
Me._pendingBranches.Add(New PendingBranch(node, Me.State, Me._nesting))
SetUnreachable()
Return Nothing
End Function
Public Overrides Function VisitContinueStatement(node As BoundContinueStatement) As BoundNode
Me._pendingBranches.Add(New PendingBranch(node, Me.State, Me._nesting))
SetUnreachable()
Return Nothing
End Function
Public Overrides Function VisitMyBaseReference(node As BoundMyBaseReference) As BoundNode
Return Nothing
End Function
Public Overrides Function VisitDoLoopStatement(node As BoundDoLoopStatement) As BoundNode
If node.ConditionOpt IsNot Nothing Then
If node.ConditionIsTop Then
VisitDoLoopTopConditionStatement(node)
Else
VisitDoLoopBottomConditionStatement(node)
End If
Else
VisitUnconditionalDoLoopStatement(node)
End If
Return Nothing
End Function
Public Sub VisitDoLoopTopConditionStatement(node As BoundDoLoopStatement)
Debug.Assert(node.ConditionIsTop AndAlso node.ConditionOpt IsNot Nothing)
' do while | until node.Condition statements node.ContinueLabel: loop node.BreakLabel:
LoopHead(node)
VisitCondition(node.ConditionOpt)
Dim exitState As LocalState
If node.ConditionIsUntil Then
exitState = Me.StateWhenTrue
Me.SetState(Me.StateWhenFalse)
Else
exitState = Me.StateWhenFalse
Me.SetState(Me.StateWhenTrue)
End If
VisitStatement(node.Body)
ResolveContinues(node.ContinueLabel)
LoopTail(node)
ResolveBreaks(exitState, node.ExitLabel)
End Sub
Public Sub VisitDoLoopBottomConditionStatement(node As BoundDoLoopStatement)
Debug.Assert(Not node.ConditionIsTop AndAlso node.ConditionOpt IsNot Nothing)
' do statements node.ContinueLabel: loop while|until node.Condition node.ExitLabel:
LoopHead(node)
VisitStatement(node.Body)
ResolveContinues(node.ContinueLabel)
VisitCondition(node.ConditionOpt)
Dim exitState As LocalState
If node.ConditionIsUntil Then
exitState = Me.StateWhenTrue
Me.SetState(Me.StateWhenFalse)
Else
exitState = Me.StateWhenFalse
Me.SetState(Me.StateWhenTrue)
End If
LoopTail(node)
ResolveBreaks(exitState, node.ExitLabel)
End Sub
Private Overloads Sub VisitUnconditionalDoLoopStatement(node As BoundDoLoopStatement)
Debug.Assert(node.ConditionOpt Is Nothing)
' do statements; node.ContinueLabel: loop node.BreakLabel:
LoopHead(node)
VisitStatement(node.Body)
ResolveContinues(node.ContinueLabel)
Dim exitState = UnreachableState()
LoopTail(node)
ResolveBreaks(exitState, node.ExitLabel)
End Sub
Public Overrides Function VisitGotoStatement(node As BoundGotoStatement) As BoundNode
Me._pendingBranches.Add(New PendingBranch(node, Me.State, Me._nesting))
SetUnreachable()
Return Nothing
End Function
Public Overrides Function VisitLabelStatement(node As BoundLabelStatement) As BoundNode
If ResolveBranches(node) Then
Me.backwardBranchChanged = True
End If
Dim label As LabelSymbol = node.Label
Dim _state As LocalState = LabelState(label)
Me.IntersectWith(Me.State, _state)
Me._labels(label) = New LabelStateAndNesting(node, Me.State.Clone(), Me._nesting)
Me._labelsSeen.Add(label)
Return Nothing
End Function
Public Overrides Function VisitThrowStatement(node As BoundThrowStatement) As BoundNode
Dim expr As BoundExpression = node.ExpressionOpt
If expr IsNot Nothing Then
VisitRvalue(expr)
End If
SetUnreachable()
Return Nothing
End Function
Public Overrides Function VisitNoOpStatement(node As BoundNoOpStatement) As BoundNode
Return Nothing
End Function
Public Overrides Function VisitNamespaceExpression(node As BoundNamespaceExpression) As BoundNode
Return Nothing
End Function
Public Overrides Function VisitFieldInitializer(node As BoundFieldInitializer) As BoundNode
VisitRvalue(node.InitialValue)
Return Nothing
End Function
Public Overrides Function VisitPropertyInitializer(node As BoundPropertyInitializer) As BoundNode
VisitRvalue(node.InitialValue)
Return Nothing
End Function
Public Overrides Function VisitArrayLength(node As BoundArrayLength) As BoundNode
VisitRvalue(node.Expression)
Return Nothing
End Function
Public Overrides Function VisitConditionalGoto(node As BoundConditionalGoto) As BoundNode
VisitCondition(node.Condition)
Debug.Assert(Me.IsConditionalState)
If node.JumpIfTrue Then
_pendingBranches.Add(New PendingBranch(node, Me.StateWhenTrue, Me._nesting))
Me.SetState(Me.StateWhenFalse)
Else
_pendingBranches.Add(New PendingBranch(node, Me.StateWhenFalse, Me._nesting))
Me.SetState(Me.StateWhenTrue)
End If
Return Nothing
End Function
Public Overrides Function VisitXmlDocument(node As BoundXmlDocument) As BoundNode
VisitRvalue(node.Declaration)
For Each child In node.ChildNodes
VisitRvalue(child)
Next
Return Nothing
End Function
Public Overrides Function VisitXmlElement(node As BoundXmlElement) As BoundNode
VisitRvalue(node.Argument)
For Each child In node.ChildNodes
VisitRvalue(child)
Next
Return Nothing
End Function
Public Overrides Function VisitXmlAttribute(node As BoundXmlAttribute) As BoundNode
VisitRvalue(node.Name)
VisitRvalue(node.Value)
Return Nothing
End Function
Public Overrides Function VisitXmlName(node As BoundXmlName) As BoundNode
Return Nothing
End Function
Public Overrides Function VisitXmlNamespace(node As BoundXmlNamespace) As BoundNode
' VisitRvalue(node.XmlNamespace) is supposed to be included in node.ObjectCreation
VisitRvalue(node.ObjectCreation)
Return Nothing
End Function
Public Overrides Function VisitXmlEmbeddedExpression(node As BoundXmlEmbeddedExpression) As BoundNode
VisitRvalue(node.Expression)
Return Nothing
End Function
Public Overrides Function VisitXmlMemberAccess(node As BoundXmlMemberAccess) As BoundNode
VisitRvalue(node.MemberAccess)
Return Nothing
End Function
Public Overrides Function VisitUnstructuredExceptionHandlingStatement(node As BoundUnstructuredExceptionHandlingStatement) As BoundNode
Visit(node.Body)
Return Nothing
End Function
Public Overrides Function VisitAwaitOperator(node As BoundAwaitOperator) As BoundNode
VisitRvalue(node.Operand)
Return Nothing
End Function
Public Overrides Function VisitNameOfOperator(node As BoundNameOfOperator) As BoundNode
Dim savedState As LocalState = Me.State.Clone()
SetUnreachable()
VisitRvalue(node.Argument)
Me.SetState(savedState)
Return Nothing
End Function
Public Overrides Function VisitTypeAsValueExpression(node As BoundTypeAsValueExpression) As BoundNode
Visit(node.Expression)
Return Nothing
End Function
Public Overrides Function VisitInterpolatedStringExpression(node As BoundInterpolatedStringExpression) As BoundNode
For Each item In node.Contents
Debug.Assert(item.Kind = BoundKind.Literal OrElse item.Kind = BoundKind.Interpolation)
Visit(item)
Next
Return Nothing
End Function
Public Overrides Function VisitInterpolation(node As BoundInterpolation) As BoundNode
VisitRvalue(node.Expression)
VisitRvalue(node.AlignmentOpt)
VisitRvalue(node.FormatStringOpt)
Return Nothing
End Function
Public Overrides Function VisitParameterEqualsValue(node As BoundParameterEqualsValue) As BoundNode
VisitRvalue(node.Value)
Return Nothing
End Function
Public Overrides Function VisitMethodDefIndex(node As BoundMethodDefIndex) As BoundNode
Return Nothing
End Function
Public Overrides Function VisitMaximumMethodDefIndex(node As BoundMaximumMethodDefIndex) As BoundNode
Return Nothing
End Function
Public Overrides Function VisitModuleVersionId(node As BoundModuleVersionId) As BoundNode
Return Nothing
End Function
Public Overrides Function VisitModuleVersionIdString(node As BoundModuleVersionIdString) As BoundNode
Return Nothing
End Function
Public Overrides Function VisitInstrumentationPayloadRoot(node As BoundInstrumentationPayloadRoot) As BoundNode
Return Nothing
End Function
Public Overrides Function VisitSourceDocumentIndex(node As BoundSourceDocumentIndex) As BoundNode
Return Nothing
End Function
#End Region
End Class
End Namespace
|