File: Lowering\LocalRewriter\LocalRewriter_UnstructuredExceptionHandling.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.PooledObjects
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 Class LocalRewriter
 
        Private Const s_activeHandler_None As Integer = 0
        Private Const s_activeHandler_ResumeNext As Integer = 1
        Private Const s_activeHandler_FirstNonReservedOnErrorGotoIndex As Integer = 2
        Private Const s_activeHandler_FirstOnErrorResumeNextIndex As Integer = -2
 
        Private Structure UnstructuredExceptionHandlingState
            Public Context As BoundUnstructuredExceptionHandlingStatement
            Public ExceptionHandlers As ArrayBuilder(Of BoundGotoStatement)
            Public ResumeTargets As ArrayBuilder(Of BoundGotoStatement)
            Public OnErrorResumeNextCount As Integer
            Public ActiveHandlerTemporary As LocalSymbol
            Public ResumeTargetTemporary As LocalSymbol
            Public CurrentStatementTemporary As LocalSymbol
            Public ResumeNextLabel As LabelSymbol
            Public ResumeLabel As LabelSymbol
        End Structure
 
        Public Overrides Function VisitUnstructuredExceptionHandlingStatement(node As BoundUnstructuredExceptionHandlingStatement) As BoundNode
            Debug.Assert(_currentLineTemporary Is Nothing)
 
            If Not node.TrackLineNumber Then
                Return RewriteUnstructuredExceptionHandlingStatementIntoBlock(node)
            End If
 
            Dim nodeFactory As New SyntheticBoundNodeFactory(_topMethod, _currentMethodOrLambda, node.Syntax, _compilationState, _diagnostics)
            Dim int32 = nodeFactory.SpecialType(SpecialType.System_Int32)
            _currentLineTemporary = New SynthesizedLocal(_topMethod, int32, SynthesizedLocalKind.OnErrorCurrentLine, DirectCast(nodeFactory.Syntax, StatementSyntax))
 
            Dim body As BoundBlock
 
            If node.ContainsOnError OrElse node.ContainsResume Then
                body = RewriteUnstructuredExceptionHandlingStatementIntoBlock(node)
            Else
                body = DirectCast(VisitBlock(node.Body), BoundBlock)
            End If
 
            body = body.Update(body.StatementListSyntax,
                               If(body.Locals.IsEmpty,
                                  ImmutableArray.Create(Of LocalSymbol)(_currentLineTemporary),
                                  body.Locals.Add(_currentLineTemporary)),
                               body.Statements)
 
            _currentLineTemporary = Nothing
 
            Return body
        End Function
 
        Private Function RewriteUnstructuredExceptionHandlingStatementIntoBlock(node As BoundUnstructuredExceptionHandlingStatement) As BoundBlock
            Debug.Assert(node.ContainsOnError OrElse node.ContainsResume)
 
            Debug.Assert(_unstructuredExceptionHandling.Context Is Nothing)
            Debug.Assert(_unstructuredExceptionHandling.ExceptionHandlers Is Nothing)
            Debug.Assert(_unstructuredExceptionHandling.ResumeTargets Is Nothing)
            Debug.Assert(_unstructuredExceptionHandling.OnErrorResumeNextCount = 0)
            Debug.Assert(_unstructuredExceptionHandling.ActiveHandlerTemporary Is Nothing)
            Debug.Assert(_unstructuredExceptionHandling.ResumeTargetTemporary Is Nothing)
            Debug.Assert(_unstructuredExceptionHandling.CurrentStatementTemporary Is Nothing)
            Debug.Assert(_unstructuredExceptionHandling.ResumeNextLabel Is Nothing)
            Debug.Assert(_unstructuredExceptionHandling.ResumeLabel Is Nothing)
 
            Debug.Assert(_currentMethodOrLambda Is _topMethod)
 
            ' We should emit code that is equivalent to the following:
            '-----------------------------------------------------------------------
            '     Dim VB$ActiveHandler As Integer
            '     Dim VB$ResumeTarget As Integer
            '     Dim VB$CurrentStatement As Integer
            '     
            '     Try
            '         <Body>
            '         Goto Done
            '
            ' ResumeSwitch: ' Destination of ResumeNext and Resume jumps
            '         Select Case If(<Resume>, VB$ResumeTarget, VB$ResumeTarget + 1)
            '             Case 0:
            '                 Goto ResumeSwitchFallThrough
            '             Case 1:
            '                 Goto <resume target #1>
            '             ...
            '         End Select
            '
            ' ResumeSwitchFallThrough:
            '         Goto OnErrorFailure
            '
            ' OnError:
            '         VB$ResumeTarget = VB$CurrentStatement
            '
            '         Select Case VB$ActiveHandler
            '             Case 0:
            '                 Goto OnErrorSwitchFallThrough
            '             Case 1:
            '                 Goto ResumeNext
            '             Case 2:
            '                 Goto <OnError Handler #1>
            '             ...
            '         End Select
            '
            ' OnErrorSwitchFallThrough:
            '         Goto OnErrorFailure
            '
            '     Catch e As Exception When (VB$ActiveHandler <> 0) And (VB$ResumeTarget = 0)
            '         Microsoft.VisualBasic.CompilerServices.ProjectData.SetProjectError(e)
            '         Goto OnError
            '     End Try
            '
            ' OnErrorFailure:
            '     Throw Microsoft.VisualBasic.CompilerServices.ProjectData.CreateProjectError(&H800A0033)
            '
            ' Done:
            '     If VB$ResumeTarget <> 0
            '         Microsoft.VisualBasic.CompilerServices.ProjectData.ClearProjectError()
            '     End If
            '-----------------------------------------------------------------------
            '
            ' When a resumable statement is rewritten, code that changes VB$CurrentStatement local is 
            ' injected before it and corresponding resume target is registered in unstructuredExceptionHandling.ResumeTargets.
            '
            ' When [On Error] statement is rewritten, code that changes VB$ActiveHandler local is injected and corresponding
            ' handler is registered in unstructuredExceptionHandling.ExceptionHandlers.
 
            Dim locals = ArrayBuilder(Of LocalSymbol).GetInstance()
 
            _unstructuredExceptionHandling.Context = node
            _unstructuredExceptionHandling.ExceptionHandlers = ArrayBuilder(Of BoundGotoStatement).GetInstance()
            _unstructuredExceptionHandling.OnErrorResumeNextCount = 0
 
            Dim nodeFactory As New SyntheticBoundNodeFactory(_topMethod, _currentMethodOrLambda, node.Syntax, _compilationState, _diagnostics)
            Dim int32 = nodeFactory.SpecialType(SpecialType.System_Int32)
            Dim bool = nodeFactory.SpecialType(SpecialType.System_Boolean)
 
            _unstructuredExceptionHandling.ActiveHandlerTemporary = New SynthesizedLocal(_topMethod, int32, SynthesizedLocalKind.OnErrorActiveHandler, DirectCast(nodeFactory.Syntax, StatementSyntax))
            locals.Add(_unstructuredExceptionHandling.ActiveHandlerTemporary)
            _unstructuredExceptionHandling.ResumeTargetTemporary = New SynthesizedLocal(_topMethod, int32, SynthesizedLocalKind.OnErrorResumeTarget, DirectCast(nodeFactory.Syntax, StatementSyntax))
            locals.Add(_unstructuredExceptionHandling.ResumeTargetTemporary)
 
            If node.ResumeWithoutLabelOpt IsNot Nothing Then
                _unstructuredExceptionHandling.CurrentStatementTemporary = New SynthesizedLocal(_topMethod, int32, SynthesizedLocalKind.OnErrorCurrentStatement, DirectCast(nodeFactory.Syntax, StatementSyntax))
                locals.Add(_unstructuredExceptionHandling.CurrentStatementTemporary)
                _unstructuredExceptionHandling.ResumeNextLabel = New GeneratedLabelSymbol("$VB$UnstructuredExceptionHandling_ResumeNext")
                _unstructuredExceptionHandling.ResumeLabel = New GeneratedLabelSymbol("$VB$UnstructuredExceptionHandling_Resume")
                _unstructuredExceptionHandling.ResumeTargets = ArrayBuilder(Of BoundGotoStatement).GetInstance()
            End If
 
            Dim statements = ArrayBuilder(Of BoundStatement).GetInstance()
 
            ' Rewrite the body.
            statements.Add(DirectCast(Visit(node.Body), BoundBlock))
 
            ' We reach this statement if there were no exceptions. 
            If Instrument Then
                statements.Add(SyntheticBoundNodeFactory.HiddenSequencePoint())
            End If
 
            If _unstructuredExceptionHandling.CurrentStatementTemporary IsNot Nothing Then
                RegisterUnstructuredExceptionHandlingResumeTarget(node.Syntax, False, statements)
            End If
 
            Dim doneLabel = New GeneratedLabelSymbol("$VB$UnstructuredExceptionHandling_Done")
            statements.Add(nodeFactory.Goto(doneLabel))
 
            Dim onErrorFailureLabel = New GeneratedLabelSymbol("$VB$UnstructuredExceptionHandling_OnErrorFailure")
 
            If node.ResumeWithoutLabelOpt IsNot Nothing Then
                Dim resumeSwitchFallThroughLabel = New GeneratedLabelSymbol("$VB$UnstructuredExceptionHandling_ResumeSwitchFallThrough")
                Dim resumeSwitchJumps(1 + _unstructuredExceptionHandling.ResumeTargets.Count - 1) As BoundGotoStatement
 
                ' The 0th Resume table entry falls through and
                ' branches to failure because the ResumeTarget should never be 0.
                resumeSwitchJumps(s_activeHandler_None) = nodeFactory.Goto(resumeSwitchFallThroughLabel)
 
                For i As Integer = 0 To _unstructuredExceptionHandling.ResumeTargets.Count - 1
                    resumeSwitchJumps(i + 1) = _unstructuredExceptionHandling.ResumeTargets(i)
                Next
 
                statements.Add(New BoundUnstructuredExceptionResumeSwitch(node.Syntax,
                                                                          nodeFactory.Local(_unstructuredExceptionHandling.ResumeTargetTemporary, isLValue:=False),
                                                                          nodeFactory.Label(_unstructuredExceptionHandling.ResumeLabel),
                                                                          nodeFactory.Label(_unstructuredExceptionHandling.ResumeNextLabel),
                                                                          resumeSwitchJumps.AsImmutableOrNull()))
 
                ' The fall through case will branch to On Error Failure which throws an internal error exception.
                ' Should never get here at runtime unless something has gone wrong.
                statements.Add(nodeFactory.Label(resumeSwitchFallThroughLabel))
                statements.Add(nodeFactory.Goto(onErrorFailureLabel))
            End If
 
            Dim onErrorLabel = New GeneratedLabelSymbol("$VB$UnstructuredExceptionHandling_OnError")
            statements.Add(nodeFactory.Label(onErrorLabel))
 
            ' Remember the current resume target.
            ' If we are not keeping track of the current line, then just
            ' store a non-zero into resumeTargetTemporary so we know we're in a
            ' handler. Otherwise, store the current line into the resume local
            ' so we know where to resume and to indicate that we're currently
            ' in a handler.
            '
            statements.Add(nodeFactory.AssignmentExpression(nodeFactory.Local(_unstructuredExceptionHandling.ResumeTargetTemporary, isLValue:=True),
                                                            If(_unstructuredExceptionHandling.CurrentStatementTemporary Is Nothing,
                                                               DirectCast(nodeFactory.Literal(-1), BoundExpression),
                                                               nodeFactory.Local(_unstructuredExceptionHandling.CurrentStatementTemporary, isLValue:=False))).ToStatement())
 
            ' Generate switch that jumps to the active handler
            Dim onErrorSwitchFallThroughLabel = New GeneratedLabelSymbol("$VB$UnstructuredExceptionHandling_OnErrorSwitchFallThrough")
            Dim onErrorSwitchJumps(2 + _unstructuredExceptionHandling.ExceptionHandlers.Count - 1) As BoundGotoStatement
 
            onErrorSwitchJumps(s_activeHandler_None) = nodeFactory.Goto(onErrorSwitchFallThroughLabel)
            onErrorSwitchJumps(s_activeHandler_ResumeNext) = nodeFactory.Goto(If(node.ResumeWithoutLabelOpt IsNot Nothing,
                                                                               _unstructuredExceptionHandling.ResumeNextLabel,
                                                                               onErrorSwitchFallThroughLabel))
 
            For i As Integer = 0 To _unstructuredExceptionHandling.ExceptionHandlers.Count - 1
                onErrorSwitchJumps(s_activeHandler_FirstNonReservedOnErrorGotoIndex + i) = _unstructuredExceptionHandling.ExceptionHandlers(i)
            Next
 
            ' When resume is present and we are not optimizing:
            ' Determine if the handler index is less than or equal to -2 (ActiveHandler_FirstOnErrorResumeNextIndex):
            ' If so, replace it with ActiveHandler_ResumeNext and jump to the switch.
            statements.Add(New BoundUnstructuredExceptionOnErrorSwitch(node.Syntax,
                                                                       If(node.ResumeWithoutLabelOpt IsNot Nothing AndAlso OptimizationLevelIsDebug,
                                                                          nodeFactory.Conditional(nodeFactory.Binary(BinaryOperatorKind.GreaterThan,
                                                                                                                     bool,
                                                                                                                     nodeFactory.Local(_unstructuredExceptionHandling.ActiveHandlerTemporary, isLValue:=False),
                                                                                                                     nodeFactory.Literal(s_activeHandler_FirstOnErrorResumeNextIndex)),
                                                                                                  nodeFactory.Local(_unstructuredExceptionHandling.ActiveHandlerTemporary, isLValue:=False),
                                                                                                  nodeFactory.Literal(s_activeHandler_ResumeNext),
                                                                                                  int32),
                                                                          DirectCast(nodeFactory.Local(_unstructuredExceptionHandling.ActiveHandlerTemporary, isLValue:=False), BoundExpression)),
                                                                       onErrorSwitchJumps.AsImmutableOrNull()))
 
            ' Something has gone wrong with the On Error mechanism if execution
            ' makes it here.  Branch to the Failure block which will throw an internal
            ' error exception.
            statements.Add(nodeFactory.Label(onErrorSwitchFallThroughLabel))
            statements.Add(nodeFactory.Goto(onErrorFailureLabel))
 
            ' Done with content of the try block.
            Dim tryBlock = nodeFactory.Block(statements.ToImmutable())
            statements.Clear()
 
            statements.Add(RewriteTryStatement(
                node.Syntax,
                tryBlock,
                ImmutableArray.Create(New BoundCatchBlock(
                    node.Syntax,
                    Nothing,
                    Nothing,
                    If(_currentLineTemporary IsNot Nothing,
                        New BoundLocal(node.Syntax, _currentLineTemporary, isLValue:=False, type:=_currentLineTemporary.Type),
                        Nothing),
                    New BoundUnstructuredExceptionHandlingCatchFilter(node.Syntax,
                        nodeFactory.Local(_unstructuredExceptionHandling.ActiveHandlerTemporary, isLValue:=False),
                        nodeFactory.Local(_unstructuredExceptionHandling.ResumeTargetTemporary, isLValue:=False),
                        bool),
                    nodeFactory.Block(ImmutableArray.Create(Of BoundStatement)(nodeFactory.Goto(onErrorLabel))),
                    isSynthesizedAsyncCatchAll:=False)),
                Nothing,
                Nothing))
 
            ' Something has gone wrong with the On Error mechanism if execution
            ' makes it here.  
            statements.Add(nodeFactory.Label(onErrorFailureLabel))
            Dim createProjectError As MethodSymbol = nodeFactory.WellKnownMember(Of MethodSymbol)(WellKnownMember.Microsoft_VisualBasic_CompilerServices_ProjectData__CreateProjectError)
 
            If createProjectError IsNot Nothing Then
                Const E_INTERNALERROR As Integer = &H800A0033 ' 51
 
                statements.Add(nodeFactory.Throw(New BoundCall(node.Syntax, createProjectError, Nothing, Nothing,
                                                               ImmutableArray.Create(Of BoundExpression)(nodeFactory.Literal(E_INTERNALERROR)),
                                                               Nothing, createProjectError.ReturnType)))
            End If
 
            statements.Add(nodeFactory.Label(doneLabel))
 
            ' Upon exiting the method, the Err object is cleared, but only if an error has occurred and it
            ' has not been "handled" (i.e., a resume, resume next, or On Error Goto -1 has not been encountered).
            ' Generate a check of the resume local;  if it's non-zero, then clear the Err object.
            Dim clearProjectError As MethodSymbol = nodeFactory.WellKnownMember(Of MethodSymbol)(WellKnownMember.Microsoft_VisualBasic_CompilerServices_ProjectData__ClearProjectError)
 
            If clearProjectError IsNot Nothing Then
                statements.Add(RewriteIfStatement(node.Syntax,
                                                  nodeFactory.Binary(BinaryOperatorKind.NotEquals,
                                                                     bool,
                                                                     nodeFactory.Local(_unstructuredExceptionHandling.ResumeTargetTemporary, isLValue:=False),
                                                                     nodeFactory.Literal(0)),
                                                  New BoundCall(node.Syntax, clearProjectError, Nothing, Nothing, ImmutableArray(Of BoundExpression).Empty, Nothing, clearProjectError.ReturnType).ToStatement(),
                                                  Nothing,
                                                  instrumentationTargetOpt:=Nothing))
            End If
 
            _unstructuredExceptionHandling.Context = Nothing
            _unstructuredExceptionHandling.ExceptionHandlers.Free()
            _unstructuredExceptionHandling.ExceptionHandlers = Nothing
 
            If _unstructuredExceptionHandling.ResumeTargets IsNot Nothing Then
                _unstructuredExceptionHandling.ResumeTargets.Free()
                _unstructuredExceptionHandling.ResumeTargets = Nothing
            End If
 
            _unstructuredExceptionHandling.ActiveHandlerTemporary = Nothing
            _unstructuredExceptionHandling.ResumeTargetTemporary = Nothing
            _unstructuredExceptionHandling.CurrentStatementTemporary = Nothing
            _unstructuredExceptionHandling.ResumeNextLabel = Nothing
            _unstructuredExceptionHandling.ResumeLabel = Nothing
            _unstructuredExceptionHandling.OnErrorResumeNextCount = 0
 
            Return nodeFactory.Block(locals.ToImmutableAndFree(), statements.ToImmutableAndFree())
        End Function
 
        Public Overrides Function VisitOnErrorStatement(node As BoundOnErrorStatement) As BoundNode
            Dim nodeFactory As New SyntheticBoundNodeFactory(_topMethod, _currentMethodOrLambda, node.Syntax, _compilationState, _diagnostics)
 
            Dim statements = ArrayBuilder(Of BoundStatement).GetInstance()
 
            If ShouldGenerateUnstructuredExceptionHandlingResumeCode(node) Then
                RegisterUnstructuredExceptionHandlingResumeTarget(node.Syntax, False, statements)
            End If
 
            ' Reset the error object
            Dim clearProjectError As MethodSymbol = nodeFactory.WellKnownMember(Of MethodSymbol)(WellKnownMember.Microsoft_VisualBasic_CompilerServices_ProjectData__ClearProjectError)
 
            If clearProjectError IsNot Nothing Then
                statements.Add(New BoundCall(node.Syntax, clearProjectError, Nothing, Nothing, ImmutableArray(Of BoundExpression).Empty, Nothing, clearProjectError.ReturnType).ToStatement)
            End If
 
            Dim newErrorHandlerIndex As Integer
 
            Select Case node.OnErrorKind
                Case OnErrorStatementKind.GoToMinusOne
                    ' Undocumented feature. -1 means to reset the handler. So, if
                    ' we're currently in a handler, this instruction resets state
                    ' to normal.
                    statements.Add(nodeFactory.AssignmentExpression(nodeFactory.Local(_unstructuredExceptionHandling.ResumeTargetTemporary, isLValue:=True),
                                                                    nodeFactory.Literal(0)).ToStatement())
                    GoTo Done
 
                Case OnErrorStatementKind.GoToLabel
                    newErrorHandlerIndex = s_activeHandler_FirstNonReservedOnErrorGotoIndex + _unstructuredExceptionHandling.ExceptionHandlers.Count
                    _unstructuredExceptionHandling.ExceptionHandlers.Add(nodeFactory.Goto(node.LabelOpt, setWasCompilerGenerated:=False))
 
                Case OnErrorStatementKind.ResumeNext
                    If OptimizationLevelIsDebug Then
                        newErrorHandlerIndex = s_activeHandler_FirstOnErrorResumeNextIndex - _unstructuredExceptionHandling.OnErrorResumeNextCount
                    Else
                        newErrorHandlerIndex = s_activeHandler_ResumeNext
                    End If
 
                    _unstructuredExceptionHandling.OnErrorResumeNextCount += 1
 
                Case Else
                    Debug.Assert(node.OnErrorKind = OnErrorStatementKind.GoToZero)
                    newErrorHandlerIndex = s_activeHandler_None
            End Select
 
            statements.Add(nodeFactory.AssignmentExpression(nodeFactory.Local(_unstructuredExceptionHandling.ActiveHandlerTemporary, isLValue:=True),
                                                            nodeFactory.Literal(newErrorHandlerIndex)).ToStatement())
Done:
            Debug.Assert(Not node.WasCompilerGenerated)
            Dim rewritten As BoundStatement = New BoundStatementList(node.Syntax, statements.ToImmutableAndFree())
 
            If Instrument(node, rewritten) Then
                rewritten = _instrumenterOpt.InstrumentOnErrorStatement(node, rewritten)
            End If
 
            Return rewritten
        End Function
 
        Public Overrides Function VisitResumeStatement(node As BoundResumeStatement) As BoundNode
            Dim nodeFactory As New SyntheticBoundNodeFactory(_topMethod, _currentMethodOrLambda, node.Syntax, _compilationState, _diagnostics)
 
            Dim statements = ArrayBuilder(Of BoundStatement).GetInstance()
 
            Dim generateUnstructuredExceptionHandlingResumeCode As Boolean = ShouldGenerateUnstructuredExceptionHandlingResumeCode(node)
            If generateUnstructuredExceptionHandlingResumeCode Then
                RegisterUnstructuredExceptionHandlingResumeTarget(node.Syntax, True, statements)
            End If
 
            ' Reset the error object
            Dim clearProjectError As MethodSymbol = nodeFactory.WellKnownMember(Of MethodSymbol)(WellKnownMember.Microsoft_VisualBasic_CompilerServices_ProjectData__ClearProjectError)
 
            If clearProjectError IsNot Nothing Then
                statements.Add(New BoundCall(node.Syntax, clearProjectError, Nothing, Nothing, ImmutableArray(Of BoundExpression).Empty, Nothing, clearProjectError.ReturnType).ToStatement)
            End If
 
            ' Emit code to check to see if we're in a handler. Compare resumeTargetTemporary against 0.
            Dim createProjectError As MethodSymbol = nodeFactory.WellKnownMember(Of MethodSymbol)(WellKnownMember.Microsoft_VisualBasic_CompilerServices_ProjectData__CreateProjectError)
 
            If createProjectError IsNot Nothing Then
                Const E_RESUMEWITHOUTERROR As Integer = &H800A0014 ' 20
 
                statements.Add(RewriteIfStatement(node.Syntax,
                                                  nodeFactory.Binary(BinaryOperatorKind.Equals,
                                                                     nodeFactory.SpecialType(SpecialType.System_Boolean),
                                                                     nodeFactory.Local(_unstructuredExceptionHandling.ResumeTargetTemporary, isLValue:=False),
                                                                     nodeFactory.Literal(0)),
                                                  nodeFactory.Throw(New BoundCall(node.Syntax, createProjectError, Nothing, Nothing,
                                                                                  ImmutableArray.Create(Of BoundExpression)(nodeFactory.Literal(E_RESUMEWITHOUTERROR)),
                                                                                  Nothing, createProjectError.ReturnType)),
                                                  Nothing,
                                                  instrumentationTargetOpt:=Nothing))
            End If
 
            ' Now generate code based on what kind of Resume we have
            Select Case node.ResumeKind
                Case ResumeStatementKind.Label
                    ' Resume label. Reset resume local
                    statements.Add(nodeFactory.AssignmentExpression(nodeFactory.Local(_unstructuredExceptionHandling.ResumeTargetTemporary, isLValue:=True),
                                                                    nodeFactory.Literal(0)).ToStatement())
 
                    If generateUnstructuredExceptionHandlingResumeCode Then
                        RegisterUnstructuredExceptionHandlingResumeTarget(node.Syntax, False, statements)
                    End If
 
                    statements.Add(nodeFactory.Goto(node.LabelOpt, setWasCompilerGenerated:=False))
 
                Case ResumeStatementKind.Next
                    statements.Add(nodeFactory.Goto(_unstructuredExceptionHandling.ResumeNextLabel))
 
                Case Else
                    Debug.Assert(node.ResumeKind = ResumeStatementKind.Plain)
                    statements.Add(nodeFactory.Goto(_unstructuredExceptionHandling.ResumeLabel))
            End Select
 
            Debug.Assert(Not node.WasCompilerGenerated)
            Dim rewritten As BoundStatement = New BoundStatementList(node.Syntax, statements.ToImmutableAndFree())
 
            If Instrument(node, rewritten) Then
                rewritten = _instrumenterOpt.InstrumentResumeStatement(node, rewritten)
            End If
 
            Return rewritten
        End Function
 
        Private Function AddResumeTargetLabel(syntax As SyntaxNode) As BoundLabelStatement
            Debug.Assert(InsideValidUnstructuredExceptionHandlingResumeContext())
            Dim targetResumeLabel = New GeneratedUnstructuredExceptionHandlingResumeLabel(_unstructuredExceptionHandling.Context.ResumeWithoutLabelOpt)
 
            _unstructuredExceptionHandling.ResumeTargets.Add(New BoundGotoStatement(syntax, targetResumeLabel, Nothing))
            Return New BoundLabelStatement(syntax, targetResumeLabel)
        End Function
 
        Private Sub AddResumeTargetLabelAndUpdateCurrentStatementTemporary(syntax As SyntaxNode, canThrow As Boolean, statements As ArrayBuilder(Of BoundStatement))
            Debug.Assert(InsideValidUnstructuredExceptionHandlingResumeContext())
            statements.Add(AddResumeTargetLabel(syntax))
 
            If canThrow Then
                Dim nodeFactory As New SyntheticBoundNodeFactory(_topMethod, _currentMethodOrLambda, syntax, _compilationState, _diagnostics)
                statements.Add(nodeFactory.AssignmentExpression(nodeFactory.Local(_unstructuredExceptionHandling.CurrentStatementTemporary, isLValue:=True),
                                                                nodeFactory.Literal(_unstructuredExceptionHandling.ResumeTargets.Count)).ToStatement())
            End If
        End Sub
 
        Private Function ShouldGenerateUnstructuredExceptionHandlingResumeCode(statement As BoundStatement) As Boolean
            If statement.WasCompilerGenerated Then
                Return False
            End If
 
#If Not DEBUG Then
            If Not InsideValidUnstructuredExceptionHandlingResumeContext() Then
                Return False
            End If
#End If
 
            If Not (TypeOf statement.Syntax Is StatementSyntax) Then
                If statement.Syntax.Parent IsNot Nothing AndAlso statement.Syntax.Parent.Kind = SyntaxKind.EraseStatement Then
                    Debug.Assert(TypeOf statement.Syntax Is ExpressionSyntax)
                Else
                    Select Case statement.Syntax.Kind
                        Case SyntaxKind.ElseIfBlock
                            If statement.Kind <> BoundKind.IfStatement Then
                                Return False
                            End If
 
                        Case SyntaxKind.CaseBlock, SyntaxKind.CaseElseBlock
                            If statement.Kind <> BoundKind.CaseBlock Then
                                Return False
                            End If
 
                        Case SyntaxKind.ModifiedIdentifier
                            If statement.Kind <> BoundKind.LocalDeclaration OrElse
                               statement.Syntax.Parent Is Nothing OrElse
                               statement.Syntax.Parent.Kind <> SyntaxKind.VariableDeclarator OrElse
                               statement.Syntax.Parent.Parent Is Nothing OrElse
                               statement.Syntax.Parent.Parent.Kind <> SyntaxKind.LocalDeclarationStatement Then
                                Return False
                            End If
 
                        Case SyntaxKind.RedimClause
                            If statement.Kind <> BoundKind.ExpressionStatement OrElse
                               Not (TypeOf statement.Syntax.Parent Is ReDimStatementSyntax) Then
                                Return False
                            End If
 
                        Case Else
                            Return False
                    End Select
                End If
            End If
 
            If TypeOf statement.Syntax Is DeclarationStatementSyntax Then
                Return False
            End If
 
#If DEBUG Then
            If _currentMethodOrLambda Is _topMethod Then
                If Not (TypeOf statement.Syntax Is ExecutableStatementSyntax) Then
                    Select Case statement.Kind
                        Case BoundKind.IfStatement
                            Debug.Assert(statement.Syntax.Kind = SyntaxKind.ElseIfBlock AndAlso
                                         statement.Syntax.Parent IsNot Nothing AndAlso
                                         statement.Syntax.Parent.Kind = SyntaxKind.MultiLineIfBlock AndAlso
                                         _unstructuredExceptionHandlingResumableStatements.ContainsKey(statement.Syntax.Parent))
 
                        Case BoundKind.CaseBlock
                            Debug.Assert((statement.Syntax.Kind = SyntaxKind.CaseBlock OrElse statement.Syntax.Kind = SyntaxKind.CaseElseBlock) AndAlso
                                         statement.Syntax.Parent IsNot Nothing AndAlso
                                         statement.Syntax.Parent.Kind = SyntaxKind.SelectBlock AndAlso
                                         _unstructuredExceptionHandlingResumableStatements.ContainsKey(statement.Syntax.Parent))
 
                        Case BoundKind.LocalDeclaration
                            Debug.Assert(statement.Syntax.Kind = SyntaxKind.ModifiedIdentifier AndAlso
                                         statement.Syntax.Parent IsNot Nothing AndAlso
                                         statement.Syntax.Parent.Kind = SyntaxKind.VariableDeclarator AndAlso
                                         statement.Syntax.Parent.Parent IsNot Nothing AndAlso
                                         statement.Syntax.Parent.Parent.Kind = SyntaxKind.LocalDeclarationStatement)
 
                        Case BoundKind.ExpressionStatement
                            Debug.Assert((statement.Syntax.Kind = SyntaxKind.RedimClause AndAlso
                                                TypeOf statement.Syntax.Parent Is ReDimStatementSyntax) OrElse
                                         (TypeOf statement.Syntax Is ExpressionSyntax AndAlso
                                                TypeOf statement.Syntax.Parent Is EraseStatementSyntax))
 
                        Case Else
                            Debug.Assert(TypeOf statement.Syntax Is ExecutableStatementSyntax)
                    End Select
                End If
 
                ' We want to throw if this function has been called for this StatementSyntax earlier.
                ' BoundStatement statement is stored to help with debugging.
                _unstructuredExceptionHandlingResumableStatements.Add(statement.Syntax, statement)
            End If
 
            Return InsideValidUnstructuredExceptionHandlingResumeContext()
#Else
            Return True
#End If
        End Function
 
        Private Structure UnstructuredExceptionHandlingContext
            Public Context As BoundUnstructuredExceptionHandlingStatement
        End Structure
 
        Private Function LeaveUnstructuredExceptionHandlingContext(node As BoundNode) As UnstructuredExceptionHandlingContext
#If DEBUG Then
            _leaveRestoreUnstructuredExceptionHandlingContextTracker.Push(node)
#End If
            Dim result As UnstructuredExceptionHandlingContext
            result.Context = _unstructuredExceptionHandling.Context
            _unstructuredExceptionHandling.Context = Nothing
            Return result
        End Function
 
        Private Sub RestoreUnstructuredExceptionHandlingContext(node As BoundNode, saved As UnstructuredExceptionHandlingContext)
#If DEBUG Then
            If _leaveRestoreUnstructuredExceptionHandlingContextTracker.Peek Is node Then
                _leaveRestoreUnstructuredExceptionHandlingContextTracker.Pop()
            Else
                Debug.Assert(_leaveRestoreUnstructuredExceptionHandlingContextTracker.Peek Is node)
            End If
#End If
            _unstructuredExceptionHandling.Context = saved.Context
        End Sub
 
        Private Function InsideValidUnstructuredExceptionHandlingResumeContext() As Boolean
            Return _unstructuredExceptionHandling.Context IsNot Nothing AndAlso
                   _unstructuredExceptionHandling.CurrentStatementTemporary IsNot Nothing AndAlso _currentMethodOrLambda Is _topMethod
        End Function
 
        Private Function InsideValidUnstructuredExceptionHandlingOnErrorContext() As Boolean
            Return _currentMethodOrLambda Is _topMethod AndAlso _unstructuredExceptionHandling.Context IsNot Nothing AndAlso _unstructuredExceptionHandling.Context.ContainsOnError
        End Function
 
        Private Sub RegisterUnstructuredExceptionHandlingResumeTarget(syntax As SyntaxNode, canThrow As Boolean, statements As ArrayBuilder(Of BoundStatement))
            AddResumeTargetLabelAndUpdateCurrentStatementTemporary(syntax, canThrow, statements)
        End Sub
 
        Private Function RegisterUnstructuredExceptionHandlingResumeTarget(syntax As SyntaxNode, node As BoundStatement, canThrow As Boolean) As BoundStatement
            Dim statements = ArrayBuilder(Of BoundStatement).GetInstance()
            AddResumeTargetLabelAndUpdateCurrentStatementTemporary(syntax, canThrow, statements)
            statements.Add(node)
            Return New BoundStatementList(syntax, statements.ToImmutableAndFree())
        End Function
 
        Private Function RegisterUnstructuredExceptionHandlingNonThrowingResumeTarget(syntax As SyntaxNode) As BoundLabelStatement
            Return AddResumeTargetLabel(syntax)
        End Function
 
        Private Function RegisterUnstructuredExceptionHandlingResumeTarget(syntax As SyntaxNode, canThrow As Boolean) As ImmutableArray(Of BoundStatement)
            Dim statements = ArrayBuilder(Of BoundStatement).GetInstance()
            AddResumeTargetLabelAndUpdateCurrentStatementTemporary(syntax, canThrow, statements)
            Return statements.ToImmutableAndFree()
        End Function
 
    End Class
End Namespace