File: Lowering\LocalRewriter\LocalRewriter_LocalDeclaration.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.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic
    Partial Friend NotInheritable Class LocalRewriter
 
        Public Overrides Function VisitLocalDeclaration(node As BoundLocalDeclaration) As BoundNode
 
            Dim localSymbol = node.LocalSymbol
            Dim staticLocalBackingFields As KeyValuePair(Of SynthesizedStaticLocalBackingField, SynthesizedStaticLocalBackingField) = Nothing
            Dim initializerOpt As BoundExpression = node.InitializerOpt
            Dim hasInitializer As Boolean = (initializerOpt IsNot Nothing)
 
            ' only if we have initializer we will produce something
            Dim result As BoundStatement = Nothing
 
            If localSymbol.IsStatic Then
                staticLocalBackingFields = CreateBackingFieldsForStaticLocal(localSymbol, hasInitializer)
            End If
 
            If hasInitializer Then
                ' Note: A variable declaration with "AsNew" and just one variable gets bound to a BoundLocalDeclaration instead of a
                ' BoundAsNewLocalDeclaration to simplify things.
                '
                ' We need to fill the replacement map in case the initializer is a object member initializer and does not need a
                ' temporary.
                Dim placeholder As BoundWithLValueExpressionPlaceholder = Nothing
 
                If initializerOpt.Kind = BoundKind.ObjectCreationExpression OrElse initializerOpt.Kind = BoundKind.NewT Then
                    Dim objectCreationExpression = DirectCast(initializerOpt, BoundObjectCreationExpressionBase)
 
                    If objectCreationExpression.InitializerOpt IsNot Nothing AndAlso
                        objectCreationExpression.InitializerOpt.Kind = BoundKind.ObjectInitializerExpression Then
 
                        Dim objectInitializer = DirectCast(objectCreationExpression.InitializerOpt, BoundObjectInitializerExpression)
 
                        If Not objectInitializer.CreateTemporaryLocalForInitialization Then
                            Debug.Assert(objectInitializer.PlaceholderOpt IsNot Nothing)
 
                            placeholder = objectInitializer.PlaceholderOpt
                            AddPlaceholderReplacement(placeholder, VisitExpressionNode(New BoundLocal(node.Syntax, localSymbol, localSymbol.Type)))
                        End If
                    End If
                End If
 
                ' Create an initializer for the local if the local is not a constant.
                If Not localSymbol.IsConst Then
                    Dim rewrittenInitializer As BoundExpression = VisitAndGenerateObjectCloneIfNeeded(initializerOpt)
                    result = RewriteLocalDeclarationAsInitializer(node, rewrittenInitializer, staticLocalBackingFields, placeholder Is Nothing)
                End If
 
                If placeholder IsNot Nothing Then
                    RemovePlaceholderReplacement(placeholder)
                End If
            End If
 
            Return result
        End Function
 
        ''' <summary>
        ''' Replaces local declaration with its initializer
        ''' Also marks resulting statement with seq point that matches original declaration.
        ''' </summary>
        Private Function RewriteLocalDeclarationAsInitializer(
            node As BoundLocalDeclaration,
            rewrittenInitializer As BoundExpression,
            staticLocalBackingFields As KeyValuePair(Of SynthesizedStaticLocalBackingField, SynthesizedStaticLocalBackingField),
            Optional objectInitializerNeedsTemporary As Boolean = True
        ) As BoundStatement
            Debug.Assert(rewrittenInitializer IsNot Nothing)
 
            Dim saveState As UnstructuredExceptionHandlingContext = LeaveUnstructuredExceptionHandlingContext(node)
 
            Dim result As BoundStatement
 
            ' If the rewritten initializer only rewrites into assignment operators, then just make it a statement and return it.
            ' This should only be the case for a AsNew declaration with a object member declaration
            If Not objectInitializerNeedsTemporary Then
 
                Debug.Assert((node.InitializerOpt Is Nothing AndAlso node.InitializedByAsNew) OrElse
                             (DirectCast(node.InitializerOpt, BoundObjectCreationExpressionBase).InitializerOpt.Kind = BoundKind.ObjectInitializerExpression AndAlso
                             Not DirectCast(DirectCast(node.InitializerOpt, BoundObjectCreationExpressionBase).InitializerOpt,
                                            BoundObjectInitializerExpression).CreateTemporaryLocalForInitialization))
 
                result = New BoundExpressionStatement(rewrittenInitializer.Syntax, rewrittenInitializer)
 
            Else
                result = New BoundExpressionStatement(
                    rewrittenInitializer.Syntax,
                    New BoundAssignmentOperator(
                        rewrittenInitializer.Syntax,
                        VisitExpressionNode(
                            New BoundLocal(
                                node.Syntax,
                                node.LocalSymbol,
                                node.LocalSymbol.Type
                            )
                        ),
                        rewrittenInitializer,
                        suppressObjectClone:=True,
                        type:=node.LocalSymbol.Type
                    )
                )
            End If
 
            If node.LocalSymbol.IsStatic Then
                result = EnforceStaticLocalInitializationSemantics(staticLocalBackingFields, result)
            End If
 
            RestoreUnstructuredExceptionHandlingContext(node, saveState)
 
            If ShouldGenerateUnstructuredExceptionHandlingResumeCode(node) Then
                result = RegisterUnstructuredExceptionHandlingResumeTarget(node.Syntax, result, canThrow:=True)
            End If
 
            If Instrument(node) Then
                result = _instrumenterOpt.InstrumentLocalInitialization(node, result)
            End If
 
            Return result
        End Function
 
        Private Function CreateBackingFieldsForStaticLocal(localSymbol As LocalSymbol, hasInitializer As Boolean) As KeyValuePair(Of SynthesizedStaticLocalBackingField, SynthesizedStaticLocalBackingField)
            Debug.Assert(localSymbol.IsStatic)
 
            If _staticLocalMap Is Nothing Then
                _staticLocalMap = New Dictionary(Of LocalSymbol, KeyValuePair(Of SynthesizedStaticLocalBackingField, SynthesizedStaticLocalBackingField))(ReferenceEqualityComparer.Instance)
            End If
 
            Dim result As New KeyValuePair(Of SynthesizedStaticLocalBackingField, SynthesizedStaticLocalBackingField)(
                                                New SynthesizedStaticLocalBackingField(localSymbol, isValueField:=True, reportErrorForLongNames:=Not hasInitializer),
                                                If(hasInitializer, New SynthesizedStaticLocalBackingField(localSymbol, isValueField:=False, reportErrorForLongNames:=True), Nothing))
 
            If _emitModule IsNot Nothing Then
                _emitModule.AddSynthesizedDefinition(Me._topMethod.ContainingType, result.Key.GetCciAdapter())
 
                If result.Value IsNot Nothing Then
                    _emitModule.AddSynthesizedDefinition(Me._topMethod.ContainingType, result.Value.GetCciAdapter())
                End If
            End If
 
            _staticLocalMap.Add(localSymbol, result)
 
            Return result
        End Function
 
        Public Overrides Function VisitLocal(node As BoundLocal) As BoundNode
            If node.LocalSymbol.IsStatic Then
                Dim backingValueField As SynthesizedStaticLocalBackingField = _staticLocalMap(node.LocalSymbol).Key
 
                Return New BoundFieldAccess(node.Syntax,
                                            If(_topMethod.IsShared,
                                               Nothing,
                                               New BoundMeReference(node.Syntax, _topMethod.ContainingType)),
                                               backingValueField, isLValue:=node.IsLValue, type:=backingValueField.Type)
            Else
                Return MyBase.VisitLocal(node)
            End If
        End Function
 
        Private Function EnforceStaticLocalInitializationSemantics(
            staticLocalBackingFields As KeyValuePair(Of SynthesizedStaticLocalBackingField, SynthesizedStaticLocalBackingField),
            rewrittenInitialization As BoundStatement
        ) As BoundStatement
            Dim syntax = rewrittenInitialization.Syntax
            Dim objectType = GetSpecialTypeWithUseSiteDiagnostics(SpecialType.System_Object, syntax)
            Dim booleanType = GetSpecialTypeWithUseSiteDiagnostics(SpecialType.System_Boolean, syntax)
 
            Dim staticLocalInitFlag__ctor As MethodSymbol = Nothing
            Dim compareExchange As MethodSymbol = Nothing
            Dim state As FieldSymbol = Nothing
            Dim ctorIncompleteInitialization As MethodSymbol = Nothing
 
            ' Note, use of 'Or' rather than 'OrElse' in the following 'If' is intentional.
            ' The goal as to report as many errors as possible, simplifies testing as well.
            If (objectType.IsErrorType() OrElse booleanType.IsErrorType()) Or
               Not TryGetWellknownMember(staticLocalInitFlag__ctor, WellKnownMember.Microsoft_VisualBasic_CompilerServices_StaticLocalInitFlag__ctor, syntax) Or
               Not TryGetWellknownMember(compareExchange, WellKnownMember.System_Threading_Interlocked__CompareExchange_T, syntax) Or
               Not TryGetWellknownMember(state, WellKnownMember.Microsoft_VisualBasic_CompilerServices_StaticLocalInitFlag__State, syntax) Or
               Not TryGetWellknownMember(ctorIncompleteInitialization, WellKnownMember.Microsoft_VisualBasic_CompilerServices_IncompleteInitialization__ctor, syntax) Then
                Return rewrittenInitialization
            End If
 
            Dim statements = ArrayBuilder(Of BoundStatement).GetInstance()
 
            Dim flag = New BoundFieldAccess(syntax,
                                            If(_topMethod.IsShared,
                                               Nothing,
                                               New BoundMeReference(syntax, _topMethod.ContainingType)),
                                            staticLocalBackingFields.Value, isLValue:=True, type:=staticLocalBackingFields.Value.Type)
 
            Dim useSiteInfo = GetNewCompoundUseSiteInfo()
            Dim flagAsObject = New BoundDirectCast(syntax,
                                                   flag.MakeRValue(),
                                                   Conversions.ClassifyDirectCastConversion(flag.Type, objectType, useSiteInfo),
                                                   objectType)
            _diagnostics.Add(syntax, useSiteInfo)
 
            ' If flag Is Nothing
            '    Interlocked.CompareExchange(flag, New StaticLocalInitFlag, Nothing)
            ' End If
 
            Dim flagIsNothing = New BoundBinaryOperator(syntax,
                                                        BinaryOperatorKind.Is, flagAsObject,
                                                        New BoundLiteral(syntax, ConstantValue.Nothing, objectType),
                                                        False,
                                                        booleanType)
 
            Dim newFlagInstance = New BoundObjectCreationExpression(syntax,
                                                                    staticLocalInitFlag__ctor,
                                                                    ImmutableArray(Of BoundExpression).Empty,
                                                                    Nothing,
                                                                    flag.Type)
 
            Dim interlockedCompareExchangeFlagWithNewInstance =
                    New BoundCall(syntax,
                                  compareExchange.Construct(flag.Type),
                                  Nothing, Nothing,
                                  ImmutableArray.Create(Of BoundExpression)(flag, newFlagInstance, New BoundLiteral(syntax, ConstantValue.Nothing, flag.Type)),
                                  Nothing,
                                  flag.Type)
 
            Dim conditionalFlagInit = RewriteIfStatement(syntax, flagIsNothing, interlockedCompareExchangeFlagWithNewInstance.ToStatement(), Nothing, instrumentationTargetOpt:=Nothing)
 
            statements.Add(conditionalFlagInit)
 
            ' Initialization of a static local occurs the first time
            ' control reaches the declaration. To guarantee this,
            ' some code to guard the execution of the initializer
            ' is necessary. For a static local named "Var", this
            ' guard code utilizes a flag named Var$Init of type
            '
            ' Class Microsoft.VisualBasic.CompilerServices.StaticLocalInitFlag
            '     Public State As Short
            ' End Class
            '
            ' and is of the form:
            '
            ' SyncLock Var$Init
            '     Try
            '         If (Var$Init.State = 0) Then
            '             Var$Init.State = 2
            '             Var = Initialization
            '         Else If (Var$Init.State = 2) Then
            '             ' Recursion in initialization
            '             Throw New Microsoft.VisualBasic.CompilerServices.IncompleteInitialization()
            '         End If
            '     Finally
            '         Var$Init.State = 1
            '     End Try
            ' End SyncLock
 
            Dim boundLockTakenLocal As BoundLocal = Nothing
            Dim tempLockTakenAssignment As BoundStatement = Nothing
 
            Dim boundMonitorEnterCall As BoundStatement = GenerateMonitorEnter(syntax, flagAsObject, boundLockTakenLocal, tempLockTakenAssignment)
            Dim flagState = New BoundFieldAccess(syntax, flag, state, isLValue:=True, type:=state.Type)
 
            Dim two = New BoundLiteral(syntax, ConstantValue.Create(2S), flagState.Type)
            Dim flagStateIsZero = New BoundBinaryOperator(syntax,
                                                          BinaryOperatorKind.Equals,
                                                          flagState.MakeRValue(),
                                                          New BoundLiteral(syntax, ConstantValue.Default(ConstantValueTypeDiscriminator.Int16), flagState.Type),
                                                          False,
                                                          booleanType)
 
            Dim flagStateAssignTwo = New BoundAssignmentOperator(syntax, flagState, two, suppressObjectClone:=True).ToStatement()
 
            Dim flagStateIsTwo = New BoundBinaryOperator(syntax,
                                                         BinaryOperatorKind.Equals,
                                                         flagState.MakeRValue(),
                                                         two,
                                                         False,
                                                         booleanType)
 
            Dim throwIncompleteInitialization = New BoundThrowStatement(syntax,
                                                                        New BoundObjectCreationExpression(syntax,
                                                                                                          ctorIncompleteInitialization,
                                                                                                          ImmutableArray(Of BoundExpression).Empty,
                                                                                                          Nothing,
                                                                                                          ctorIncompleteInitialization.ContainingType))
 
            Dim conditionalValueInit =
                RewriteIfStatement(syntax,
                                   flagStateIsZero,
                                   New BoundStatementList(syntax, ImmutableArray.Create(flagStateAssignTwo, rewrittenInitialization)),
                                   RewriteIfStatement(syntax,
                                                      flagStateIsTwo,
                                                      throwIncompleteInitialization,
                                                      Nothing,
                                                      instrumentationTargetOpt:=Nothing),
                                   instrumentationTargetOpt:=Nothing)
 
            Dim locals As ImmutableArray(Of LocalSymbol)
            Dim statementsInTry As ImmutableArray(Of BoundStatement)
 
            If boundLockTakenLocal IsNot Nothing Then
                locals = ImmutableArray.Create(boundLockTakenLocal.LocalSymbol)
                statements.Add(tempLockTakenAssignment)
                statementsInTry = ImmutableArray.Create(boundMonitorEnterCall, conditionalValueInit)
            Else
                locals = ImmutableArray(Of LocalSymbol).Empty
                statements.Add(boundMonitorEnterCall)
                statementsInTry = ImmutableArray.Create(Of BoundStatement)(conditionalValueInit)
            End If
 
            Dim tryBody As BoundBlock = New BoundBlock(syntax,
                                                       Nothing,
                                                       ImmutableArray(Of LocalSymbol).Empty,
                                                       statementsInTry)
 
            Dim flagStateAssignOne = New BoundAssignmentOperator(syntax, flagState,
                                                                 New BoundLiteral(syntax, ConstantValue.Create(1S), flagState.Type),
                                                                 suppressObjectClone:=True).ToStatement()
 
            Dim monitorExit As BoundStatement = GenerateMonitorExit(syntax, flagAsObject, boundLockTakenLocal)
 
            Dim finallyBody As BoundBlock = New BoundBlock(syntax,
                                                           Nothing,
                                                           ImmutableArray(Of LocalSymbol).Empty,
                                                           ImmutableArray.Create(flagStateAssignOne, monitorExit))
 
            Dim tryFinally = New BoundTryStatement(syntax, tryBody, ImmutableArray(Of BoundCatchBlock).Empty, finallyBody, Nothing)
 
            statements.Add(tryFinally)
 
            Return New BoundBlock(syntax,
                                  Nothing,
                                  locals,
                                  statements.ToImmutableAndFree)
        End Function
 
    End Class
End Namespace