File: Lowering\LocalRewriter\LocalRewriter_While.vb
Web Access
Project: src\roslyn\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