File: Lowering\LocalRewriter\LocalRewriter_While.vb
Web Access
Project: src\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
 
Imports System.Collections.Immutable
Imports System.Diagnostics
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports TypeKind = Microsoft.CodeAnalysis.TypeKind
 
Namespace Microsoft.CodeAnalysis.VisualBasic
    Partial Friend NotInheritable Class LocalRewriter
        Public Overrides Function VisitWhileStatement(node As BoundWhileStatement) As BoundNode
 
            Dim generateUnstructuredExceptionHandlingResumeCode As Boolean = ShouldGenerateUnstructuredExceptionHandlingResumeCode(node)
 
            Dim loopResumeLabel As BoundLabelStatement = Nothing
            Dim conditionResumeTarget As ImmutableArray(Of BoundStatement) = Nothing
 
            If generateUnstructuredExceptionHandlingResumeCode Then
                loopResumeLabel = RegisterUnstructuredExceptionHandlingNonThrowingResumeTarget(node.Syntax)
                conditionResumeTarget = RegisterUnstructuredExceptionHandlingResumeTarget(node.Syntax, canThrow:=True)
            End If
 
            Dim rewrittenBody = DirectCast(Visit(node.Body), BoundStatement)
 
            Dim afterBodyResumeLabel As BoundLabelStatement = Nothing
 
            If generateUnstructuredExceptionHandlingResumeCode Then
                afterBodyResumeLabel = RegisterUnstructuredExceptionHandlingNonThrowingResumeTarget(node.Syntax)
            End If
 
            Return RewriteWhileStatement(node,
                                         VisitExpressionNode(node.Condition),
                                         rewrittenBody,
                                         node.ContinueLabel,
                                         node.ExitLabel,
                                         True,
                                         loopResumeLabel,
                                         conditionResumeTarget,
                                         afterBodyResumeLabel)
        End Function
 
        Protected Function RewriteWhileStatement(
            statement As BoundStatement,
            rewrittenCondition As BoundExpression,
            rewrittenBody As BoundStatement,
            continueLabel As LabelSymbol,
            exitLabel As LabelSymbol,
            Optional loopIfTrue As Boolean = True,
            Optional loopResumeLabelOpt As BoundLabelStatement = Nothing,
            Optional conditionResumeTargetOpt As ImmutableArray(Of BoundStatement) = Nothing,
            Optional afterBodyResumeTargetOpt As BoundStatement = Nothing
        ) As BoundNode
            Dim startLabel = GenerateLabel("start")
            Dim statementSyntax = statement.Syntax
 
            Dim instrument As Boolean = Me.Instrument(statement)
 
            If instrument Then
                Select Case statement.Kind
                    Case BoundKind.WhileStatement
                        afterBodyResumeTargetOpt = _instrumenterOpt.InstrumentWhileEpilogue(DirectCast(statement, BoundWhileStatement), afterBodyResumeTargetOpt)
                    Case BoundKind.DoLoopStatement
                        afterBodyResumeTargetOpt = _instrumenterOpt.InstrumentDoLoopEpilogue(DirectCast(statement, BoundDoLoopStatement), afterBodyResumeTargetOpt)
                    Case BoundKind.ForEachStatement
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(statement.Kind)
                End Select
            End If
 
            rewrittenBody = Concat(rewrittenBody, afterBodyResumeTargetOpt)
 
            ' 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.
            If rewrittenCondition IsNot Nothing AndAlso instrument Then
                Select Case statement.Kind
                    Case BoundKind.WhileStatement
                        rewrittenCondition = _instrumenterOpt.InstrumentWhileStatementCondition(DirectCast(statement, BoundWhileStatement), rewrittenCondition, _currentMethodOrLambda)
                    Case BoundKind.DoLoopStatement
                        rewrittenCondition = _instrumenterOpt.InstrumentDoLoopStatementCondition(DirectCast(statement, BoundDoLoopStatement), rewrittenCondition, _currentMethodOrLambda)
                    Case BoundKind.ForEachStatement
                        rewrittenCondition = _instrumenterOpt.InstrumentForEachStatementCondition(DirectCast(statement, BoundForEachStatement), rewrittenCondition, _currentMethodOrLambda)
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(statement.Kind)
                End Select
            End If
 
            Dim ifConditionGotoStart As BoundStatement = New BoundConditionalGoto(
                statementSyntax,
                rewrittenCondition,
                loopIfTrue,
                startLabel)
 
            If Not conditionResumeTargetOpt.IsDefaultOrEmpty Then
                ifConditionGotoStart = New BoundStatementList(ifConditionGotoStart.Syntax, conditionResumeTargetOpt.Add(ifConditionGotoStart))
            End If
 
            If instrument Then
                Select Case statement.Kind
                    Case BoundKind.WhileStatement
                        ifConditionGotoStart = _instrumenterOpt.InstrumentWhileStatementConditionalGotoStart(DirectCast(statement, BoundWhileStatement), ifConditionGotoStart)
                    Case BoundKind.DoLoopStatement
                        ifConditionGotoStart = _instrumenterOpt.InstrumentDoLoopStatementEntryOrConditionalGotoStart(DirectCast(statement, BoundDoLoopStatement), ifConditionGotoStart)
                    Case BoundKind.ForEachStatement
                        ifConditionGotoStart = _instrumenterOpt.InstrumentForEachStatementConditionalGotoStart(DirectCast(statement, BoundForEachStatement), ifConditionGotoStart)
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(statement.Kind)
                End Select
            End If
 
            ' While condition
            '    body
            ' End While
            '
            ' becomes
            '
            ' goto continue;
            ' start:
            ' body
            ' continue:
            ' {GotoIfTrue condition start}
            ' exit:
 
            'mark the initial jump as hidden.
            'We do not want to associate it with statement before.
            'This jump may be a target of another jump (for example if loops are nested) and that will make 
            'impression of the previous statement being re-executed
            Dim gotoContinue As BoundStatement = New BoundGotoStatement(statementSyntax, continueLabel, Nothing)
 
            If loopResumeLabelOpt IsNot Nothing Then
                gotoContinue = Concat(loopResumeLabelOpt, gotoContinue)
            End If
 
            If instrument Then
                gotoContinue = SyntheticBoundNodeFactory.HiddenSequencePoint(gotoContinue)
            End If
 
            Return New BoundStatementList(statementSyntax, ImmutableArray.Create(Of BoundStatement)(
                    gotoContinue,
                    New BoundLabelStatement(statementSyntax, startLabel),
                    rewrittenBody,
                    New BoundLabelStatement(statementSyntax, continueLabel),
                    ifConditionGotoStart,
                    New BoundLabelStatement(statementSyntax, exitLabel)
                ))
 
        End Function
    End Class
End Namespace