|
' 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
Imports ReferenceEqualityComparer = Roslyn.Utilities.ReferenceEqualityComparer
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
|