File: Lowering\LocalRewriter\LocalRewriter_If.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 Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic
    Partial Friend NotInheritable Class LocalRewriter
        Public Overrides Function VisitIfStatement(node As BoundIfStatement) As BoundNode
            Dim syntax = node.Syntax
 
            Dim generateUnstructuredExceptionHandlingResumeCode As Boolean = ShouldGenerateUnstructuredExceptionHandlingResumeCode(node)
            Dim unstructuredExceptionHandlingResumeTarget As ImmutableArray(Of BoundStatement) = Nothing
 
            If generateUnstructuredExceptionHandlingResumeCode Then
                unstructuredExceptionHandlingResumeTarget = RegisterUnstructuredExceptionHandlingResumeTarget(syntax, canThrow:=True)
            End If
 
            ' == Rewrite Condition
            Dim newCondition = VisitExpressionNode(node.Condition)
 
            ' == Rewrite Consequence
            Dim newConsequence As BoundStatement = DirectCast(Visit(node.Consequence), BoundStatement)
 
            ' Update the resume table so exceptions that occur in the [Consequence] don't fall through to 
            ' [AlternativeOpt] upon Resume Next and to make sure that we are in the right scope when we 
            ' Resume Next on [End If].
            Dim finishConsequenceWithResumeTarget As Boolean = (node.AlternativeOpt IsNot Nothing)
 
            ' need to add a sequence point before the body
            ' make sure SP is outside of the block if consequence is a block
            ' also add SP for End If after the consequence
            ' make sure SP is inside the block if the consequence is a block
            ' we must still have the block in scope when stopped on End If
            Dim instrument As Boolean = Me.Instrument(node)
 
            If instrument Then
                Dim resumeTarget As BoundStatement = Nothing
 
                Select Case syntax.Kind
                    Case SyntaxKind.MultiLineIfBlock,
                         SyntaxKind.ElseIfBlock
                        If generateUnstructuredExceptionHandlingResumeCode AndAlso (OptimizationLevelIsDebug OrElse finishConsequenceWithResumeTarget) Then
                            resumeTarget = RegisterUnstructuredExceptionHandlingNonThrowingResumeTarget(newConsequence.Syntax)
                        End If
 
                        finishConsequenceWithResumeTarget = False
 
                    Case SyntaxKind.SingleLineIfStatement
                        ' single line if has no EndIf
 
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(syntax.Kind)
                End Select
 
                newConsequence = Concat(newConsequence, _instrumenterOpt.InstrumentIfStatementConsequenceEpilogue(node, resumeTarget))
            End If
 
            If generateUnstructuredExceptionHandlingResumeCode AndAlso finishConsequenceWithResumeTarget Then
                newConsequence = Concat(newConsequence, RegisterUnstructuredExceptionHandlingNonThrowingResumeTarget(newConsequence.Syntax))
            End If
 
            ' == Rewrite Else
            Dim newAlternative As BoundStatement = DirectCast(Visit(node.AlternativeOpt), BoundStatement)
 
            If instrument AndAlso newAlternative IsNot Nothing Then
                If syntax.Kind <> SyntaxKind.SingleLineIfStatement Then
                    Dim asElse = TryCast(node.AlternativeOpt.Syntax, ElseBlockSyntax)
                    If asElse IsNot Nothing Then
                        ' Update the resume table to make sure that we are in the right scope when we Resume Next on [End If].
                        Dim resumeTarget As BoundStatement = Nothing
                        If generateUnstructuredExceptionHandlingResumeCode AndAlso OptimizationLevelIsDebug Then
                            resumeTarget = RegisterUnstructuredExceptionHandlingNonThrowingResumeTarget(newAlternative.Syntax)
                        End If
 
                        newAlternative = Concat(newAlternative,
                                                _instrumenterOpt.InstrumentIfStatementAlternativeEpilogue(node, resumeTarget))
                        newAlternative = PrependWithPrologue(newAlternative, _instrumenterOpt.CreateIfStatementAlternativePrologue(node))
                    End If
                Else
                    Dim asElse = TryCast(node.AlternativeOpt.Syntax, SingleLineElseClauseSyntax)
                    If asElse IsNot Nothing Then
                        newAlternative = PrependWithPrologue(newAlternative, _instrumenterOpt.CreateIfStatementAlternativePrologue(node))
                        ' single line if has no EndIf
                    End If
                End If
            End If
 
            ' == Rewrite the whole node
 
            ' EnC: We need to insert a hidden sequence point to handle function remapping in case 
            ' the containing method is edited while methods invoked in the condition are being executed.
            Debug.Assert(newCondition IsNot Nothing)
            If instrument Then
                newCondition = _instrumenterOpt.InstrumentIfStatementCondition(node, newCondition, _currentMethodOrLambda)
            End If
 
            Dim result As BoundStatement = RewriteIfStatement(
                node.Syntax,
                newCondition,
                newConsequence,
                newAlternative,
                instrumentationTargetOpt:=If(instrument, node, Nothing),
                unstructuredExceptionHandlingResumeTarget:=unstructuredExceptionHandlingResumeTarget)
 
            Return result
        End Function
 
        Private Function RewriteIfStatement(
            syntaxNode As SyntaxNode,
            rewrittenCondition As BoundExpression,
            rewrittenConsequence As BoundStatement,
            rewrittenAlternative As BoundStatement,
            instrumentationTargetOpt As BoundStatement,
            Optional unstructuredExceptionHandlingResumeTarget As ImmutableArray(Of BoundStatement) = Nothing
        ) As BoundStatement
            Debug.Assert(unstructuredExceptionHandlingResumeTarget.IsDefaultOrEmpty OrElse instrumentationTargetOpt IsNot Nothing)
 
            ' Note that ElseIf clauses are transformed into a nested if inside an else at the bound tree generation, so 
            ' a BoundIfStatement does not contain ElseIf clauses.
 
            Dim afterif = GenerateLabel("afterif")
 
            Dim afterIfStatement As BoundStatement = New BoundLabelStatement(syntaxNode, afterif)
 
            If rewrittenAlternative Is Nothing Then
                ' if (condition) 
                '   consequence;  
                '
                ' becomes
                '
                ' GotoIfFalse condition afterif;
                ' consequence;
                ' afterif:
 
                Dim condGoto As BoundStatement = New BoundConditionalGoto(syntaxNode, rewrittenCondition, False, afterif)
 
                If Not unstructuredExceptionHandlingResumeTarget.IsDefaultOrEmpty Then
                    condGoto = New BoundStatementList(condGoto.Syntax, unstructuredExceptionHandlingResumeTarget.Add(condGoto))
                End If
 
                If instrumentationTargetOpt IsNot Nothing Then
 
                    Select Case instrumentationTargetOpt.Syntax.Kind
                        Case SyntaxKind.MultiLineIfBlock,
                             SyntaxKind.ElseIfBlock,
                             SyntaxKind.SingleLineIfStatement
                            condGoto = _instrumenterOpt.InstrumentIfStatementConditionalGoto(DirectCast(instrumentationTargetOpt, BoundIfStatement), condGoto)
                        Case SyntaxKind.CaseBlock
                            condGoto = _instrumenterOpt.InstrumentCaseBlockConditionalGoto(DirectCast(instrumentationTargetOpt, BoundCaseBlock), condGoto)
                        Case Else
                            Throw ExceptionUtilities.UnexpectedValue(instrumentationTargetOpt.Syntax.Kind)
                    End Select
 
                    If instrumentationTargetOpt.Syntax.Kind = SyntaxKind.MultiLineIfBlock Then
                        ' If it is a multiline If and there is no else, associate afterIf with EndIf
                        afterIfStatement = _instrumenterOpt.InstrumentIfStatementAfterIfStatement(DirectCast(instrumentationTargetOpt, BoundIfStatement), afterIfStatement)
                    Else
                        ' otherwise hide afterif (so that it does not associate with if body).
                        afterIfStatement = SyntheticBoundNodeFactory.HiddenSequencePoint(afterIfStatement)
                    End If
                End If
 
                Return New BoundStatementList(syntaxNode, ImmutableArray.Create(condGoto, rewrittenConsequence, afterIfStatement))
            Else
                ' if (condition)
                '     consequence;
                ' else 
                '     alternative
                '
                ' becomes
                '
                ' GotoIfFalse condition alt;
                ' consequence
                ' goto afterif;
                ' alt:
                ' alternative;
                ' afterif:
 
                Dim alt = GenerateLabel("alternative")
                Dim condGoto As BoundStatement = New BoundConditionalGoto(syntaxNode, rewrittenCondition, False, alt)
 
                If Not unstructuredExceptionHandlingResumeTarget.IsDefaultOrEmpty Then
                    condGoto = New BoundStatementList(condGoto.Syntax, unstructuredExceptionHandlingResumeTarget.Add(condGoto))
                End If
 
                If instrumentationTargetOpt IsNot Nothing Then
                    Select Case instrumentationTargetOpt.Syntax.Kind
                        Case SyntaxKind.MultiLineIfBlock,
                             SyntaxKind.ElseIfBlock,
                             SyntaxKind.SingleLineIfStatement
                            condGoto = _instrumenterOpt.InstrumentIfStatementConditionalGoto(DirectCast(instrumentationTargetOpt, BoundIfStatement), condGoto)
                        Case SyntaxKind.CaseBlock
                            condGoto = _instrumenterOpt.InstrumentCaseBlockConditionalGoto(DirectCast(instrumentationTargetOpt, BoundCaseBlock), condGoto)
                        Case Else
                            Throw ExceptionUtilities.UnexpectedValue(instrumentationTargetOpt.Syntax.Kind)
                    End Select
                End If
 
                Return New BoundStatementList(syntaxNode, ImmutableArray.Create(Of BoundStatement)(
                                              condGoto,
                                              rewrittenConsequence,
                                              New BoundGotoStatement(syntaxNode, afterif, Nothing),
                                              New BoundLabelStatement(syntaxNode, alt),
                                              rewrittenAlternative,
                                              afterIfStatement))
            End If
        End Function
 
    End Class
End Namespace