File: Lowering\LocalRewriter\LocalRewriter_With.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 NotInheritable Class LocalRewriter
 
        Public Overrides Function VisitWithStatement(node As BoundWithStatement) As BoundNode
            If node.HasErrors Then
                Return node
            End If
 
            Dim saveState As UnstructuredExceptionHandlingContext = LeaveUnstructuredExceptionHandlingContext(node)
 
            ' With block attributes
            Dim rewrittenExpression As BoundExpression = VisitExpressionNode(node.OriginalExpression)
            Dim type As TypeSymbol = rewrittenExpression.Type
            Dim statementSyntax = DirectCast(node.Syntax, WithBlockSyntax).WithStatement
 
            Dim doNotUseByRefLocal = Me._currentMethodOrLambda.IsIterator OrElse
                                    Me._currentMethodOrLambda.IsAsync OrElse
                                    node.Binder.ExpressionIsAccessedFromNestedLambda
 
            ' What the placeholder should be replaced with
            Dim result As WithExpressionRewriter.Result =
                (New WithExpressionRewriter(statementSyntax)).AnalyzeWithExpression(Me._currentMethodOrLambda,
                                                             rewrittenExpression,
                                                             doNotUseByRefLocal,
                                                             isDraftRewrite:=False,
                                                             Nothing)
 
            RestoreUnstructuredExceptionHandlingContext(node, saveState)
 
            Return RewriteWithBlockStatements(node,
                                              ShouldGenerateUnstructuredExceptionHandlingResumeCode(node),
                                              result.Locals,
                                              result.Initializers,
                                              node.ExpressionPlaceholder,
                                              result.Expression)
        End Function
 
        Private Function RewriteWithBlockStatements(node As BoundWithStatement,
                                                    generateUnstructuredExceptionHandlingResumeCode As Boolean,
                                                    locals As ImmutableArray(Of LocalSymbol),
                                                    initializers As ImmutableArray(Of BoundExpression),
                                                    placeholder As BoundValuePlaceholderBase,
                                                    replaceWith As BoundExpression) As BoundBlock
            Debug.Assert(Not locals.IsDefault)
            Debug.Assert(Not initializers.IsDefault)
            Debug.Assert(placeholder IsNot Nothing)
            Debug.Assert(replaceWith IsNot Nothing)
 
            Dim block As BoundBlock = node.Body
            Dim syntax As SyntaxNode = node.Syntax
 
            ' We need to create a new Block with locals, initialization 
            ' statements, bound block and optional clean-up statements
            Dim initStatements = ArrayBuilder(Of BoundStatement).GetInstance
            Dim instrument As Boolean = Me.Instrument(node) AndAlso syntax.Kind = SyntaxKind.WithBlock
 
            If instrument Then
                Dim prologue = _instrumenterOpt.CreateWithStatementPrologue(node)
                If prologue IsNot Nothing Then
                    initStatements.Add(prologue)
                End If
            End If
 
            If generateUnstructuredExceptionHandlingResumeCode Then
                RegisterUnstructuredExceptionHandlingResumeTarget(syntax, canThrow:=True, statements:=initStatements)
            End If
 
            ' Initializers first
            For Each initializer In initializers
                initStatements.Add(New BoundExpressionStatement(syntax, initializer).MakeCompilerGenerated())
            Next
 
            ' place placeholder into the map
            AddPlaceholderReplacement(placeholder, replaceWith)
 
            ' Then the bound block 
            initStatements.Add(DirectCast(Visit(block), BoundStatement))
            RemovePlaceholderReplacement(placeholder)
 
            If instrument Then
                Dim epilogue = _instrumenterOpt.CreateWithStatementEpilogue(node)
                If epilogue IsNot Nothing Then
                    initStatements.Add(epilogue)
                End If
            End If
 
            If generateUnstructuredExceptionHandlingResumeCode Then
                initStatements.Add(RegisterUnstructuredExceptionHandlingNonThrowingResumeTarget(syntax))
            End If
 
            ' Cleanup code for locals which need it
            If Not node.Binder.ExpressionIsAccessedFromNestedLambda Then
                For Each _local In locals
                    Dim localType As TypeSymbol = _local.Type
 
                    ' Only for locals of reference type or type parameter type or non-primitive structs 
                    If Not _local.IsByRef AndAlso LocalOrFieldNeedsToBeCleanedUp(localType) Then
                        initStatements.Add(
                            New BoundExpressionStatement(
                                syntax,
                                VisitExpression(
                                    New BoundAssignmentOperator(
                                        syntax,
                                        New BoundLocal(syntax, _local, isLValue:=True, type:=localType).MakeCompilerGenerated(),
                                        New BoundConversion(
                                            syntax,
                                            New BoundLiteral(syntax, ConstantValue.Nothing, Nothing).MakeCompilerGenerated(),
                                            ConversionKind.WideningNothingLiteral,
                                            checked:=False,
                                            explicitCastInCode:=False,
                                            type:=localType).MakeCompilerGenerated(),
                                        suppressObjectClone:=True,
                                        type:=localType
                                    ).MakeCompilerGenerated()
                                )
                            ).MakeCompilerGenerated()
                        )
                    End If
                Next
            End If
 
            ' Create a new block
            Dim newBlock As New BoundBlock(syntax, Nothing, locals, initStatements.ToImmutableAndFree())
 
            Return newBlock
        End Function
 
        ''' <summary>
        ''' Cache of value types which were already calculated by LocalOrFieldNeedsToBeCleanedUp 
        ''' in this lowering, serves as an optimization 
        ''' </summary>
        Private ReadOnly _valueTypesCleanUpCache As New Dictionary(Of TypeSymbol, Boolean)
 
        Private Function LocalOrFieldNeedsToBeCleanedUp(currentType As TypeSymbol) As Boolean
            Debug.Assert(currentType IsNot Nothing)
 
            ' Locals of reference type always need to be cleaned after With statement
            If currentType.IsReferenceType OrElse currentType.IsTypeParameter Then
                Return True
            End If
 
            Debug.Assert(currentType.IsValueType)
 
            ' Locals of intrinsic and enum types does not need to be cleaned 
            If currentType.IsIntrinsicOrEnumType Then
                Return False
            End If
 
            ' Was it calculated before
            Dim _value As Boolean
            If _valueTypesCleanUpCache.TryGetValue(currentType, _value) Then
                Return _value
            End If
 
            ' NOTE: We assume here that the structure DOES NOT need to be cleaned up; 
            '       in recursive structures this will prevent recursion and return False
            _valueTypesCleanUpCache(currentType) = False
 
            ' Other structures need to be analyzed field-by-field: if there is any field 
            ' which needs to be cleaned up, the whole structure needs to be cleaned up.
            For Each member In currentType.GetMembers()
                If member.Kind = SymbolKind.Field Then
                    Dim fieldType = DirectCast(member, FieldSymbol).Type
                    Debug.Assert(fieldType IsNot Nothing)
 
                    ' If this is not the current type (optimization) and also it was not analyzed before
                    If fieldType IsNot currentType Then
 
                        ' If the field need to be cleaned up, stop iterations
                        If LocalOrFieldNeedsToBeCleanedUp(fieldType) Then
                            _valueTypesCleanUpCache(currentType) = True
                            Return True
                        End If
                    End If
 
                End If
            Next
 
            ' Value type does not need to be cleaned-up if there are no fields requiring clean-up
            Debug.Assert(Not _valueTypesCleanUpCache(currentType))
            Return False
        End Function
 
        Public Overrides Function VisitWithLValueExpressionPlaceholder(node As BoundWithLValueExpressionPlaceholder) As BoundNode
            Return PlaceholderReplacement(node)
        End Function
 
        Public Overrides Function VisitWithRValueExpressionPlaceholder(node As BoundWithRValueExpressionPlaceholder) As BoundNode
            Return PlaceholderReplacement(node)
        End Function
 
    End Class
End Namespace