|
' 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.Generic
Imports System.Collections.Immutable
Imports System.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.Cci
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Collections
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.CodeGen
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports TypeKind = Microsoft.CodeAnalysis.TypeKind
Namespace Microsoft.CodeAnalysis.VisualBasic
Partial Friend NotInheritable Class AsyncRewriter
Inherits StateMachineRewriter(Of CapturedSymbolOrExpression)
Partial Friend Class AsyncMethodToClassRewriter
Inherits StateMachineMethodToClassRewriter
Public Function VisitExpression(expression As BoundExpression) As BoundExpression
Return DirectCast(Me.Visit(expression), BoundExpression)
End Function
Public Overrides Function VisitSpillSequence(node As BoundSpillSequence) As BoundNode
Dim statements As ImmutableArray(Of BoundStatement) = Me.VisitList(node.Statements)
Dim valueOpt As BoundExpression = Me.VisitExpression(node.ValueOpt)
Dim rewrittenType As TypeSymbol = VisitType(node.Type)
If valueOpt Is Nothing OrElse valueOpt.Kind <> BoundKind.SpillSequence Then
Return node.Update(node.Locals, node.SpillFields, statements, valueOpt, rewrittenType)
End If
Dim spillSeq = DirectCast(valueOpt, BoundSpillSequence)
Debug.Assert(TypeSymbol.Equals(rewrittenType, spillSeq.Type, TypeCompareKind.ConsiderEverything))
Return node.Update(
node.Locals.Concat(spillSeq.Locals),
node.SpillFields.Concat(spillSeq.SpillFields),
statements.Concat(spillSeq.Statements),
spillSeq.ValueOpt,
rewrittenType)
End Function
Public Overrides Function VisitSequence(node As BoundSequence) As BoundNode
Dim rewritten = DirectCast(MyBase.VisitSequence(node), BoundSequence)
Dim locals As ImmutableArray(Of LocalSymbol) = rewritten.Locals
Dim sideEffects As ImmutableArray(Of BoundExpression) = rewritten.SideEffects
Dim valueOpt As BoundExpression = rewritten.ValueOpt
Dim sideEffectsRequireSpill As Boolean = NeedsSpill(sideEffects)
Dim valueRequiresSpill As Boolean = NeedsSpill(valueOpt)
If Not sideEffectsRequireSpill AndAlso Not valueRequiresSpill Then
Return rewritten
End If
Dim builder As New SpillBuilder()
builder.AddLocals(locals)
If sideEffectsRequireSpill Then
For Each sideEffect In sideEffects
builder.AddStatement(MakeExpressionStatement(sideEffect, builder))
Next
End If
If valueRequiresSpill Then
Dim spill = DirectCast(valueOpt, BoundSpillSequence)
builder.AddSpill(spill)
valueOpt = spill.ValueOpt
End If
Return builder.BuildSequenceAndFree(Me.F, valueOpt)
End Function
Private Function MakeExpressionStatement(expression As BoundExpression, ByRef builder As SpillBuilder) As BoundStatement
If NeedsSpill(expression) Then
Debug.Assert(expression.Kind = BoundKind.SpillSequence)
Dim spill = DirectCast(expression, BoundSpillSequence)
builder.AssumeFieldsIfNeeded(spill)
Return Me.RewriteSpillSequenceIntoBlock(spill, True)
Else
Return Me.F.ExpressionStatement(expression)
End If
End Function
Public Overrides Function VisitCall(node As BoundCall) As BoundNode
Dim rewritten = DirectCast(MyBase.VisitCall(node), BoundCall)
Dim receiverOpt As BoundExpression = rewritten.ReceiverOpt
Dim arguments As ImmutableArray(Of BoundExpression) = rewritten.Arguments
Dim builder As SpillBuilder
If Not NeedsSpill(arguments) Then
If Not NeedsSpill(receiverOpt) Then
Return rewritten
End If
builder = New SpillBuilder()
Dim spill = DirectCast(receiverOpt, BoundSpillSequence)
builder.AddSpill(spill)
receiverOpt = spill.ValueOpt
Else
builder = New SpillBuilder()
If Not Unspillable(receiverOpt) Then
If CodeGenerator.IsPossibleReferenceTypeReceiverOfConstrainedCall(receiverOpt) AndAlso
Not CodeGenerator.ReceiverIsKnownToReferToTempIfReferenceType(receiverOpt) AndAlso
Not CodeGenerator.IsSafeToDereferenceReceiverRefAfterEvaluatingArguments(node.Arguments) Then
Dim receiverType = receiverOpt.Type
If receiverType.IsReferenceType Then
receiverOpt = SpillRValue(receiverOpt.MakeRValue(), builder:=builder)
Else
receiverOpt = SpillLValue(receiverOpt, isReceiver:=True, evaluateSideEffects:=True, builder:=builder)
End If
Dim conditionalReceiver = TryCast(node.ReceiverOpt, BoundConditionalAccessReceiverPlaceholder)
If conditionalReceiver IsNot Nothing Then
If _conditionalAccessReceiverPlaceholderReplacementInfo Is Nothing OrElse
_conditionalAccessReceiverPlaceholderReplacementInfo.PlaceholderId <> conditionalReceiver.PlaceholderId Then
Throw ExceptionUtilities.Unreachable
End If
Debug.Assert(_conditionalAccessReceiverPlaceholderReplacementInfo.IsSpilled)
Debug.Assert(Not _conditionalAccessReceiverPlaceholderReplacementInfo.ForceCaptureIfReferenceType)
_conditionalAccessReceiverPlaceholderReplacementInfo.ForceCaptureIfReferenceType = True
ElseIf Not receiverType.IsReferenceType Then
' A case where T is actually a class must be handled specially.
' Taking a reference to a class instance is fragile because the value behind the
' reference might change while arguments are evaluated. However, the call should be
' performed on the instance that is behind reference at the time we push the
' reference to the stack. So, for a class we need to emit a reference to a temporary
' location, rather than to the original location
Dim referenceReceiverBuilder As New SpillBuilder()
Dim spilledReferenceReceiver As BoundExpression = SpillRValue(receiverOpt.MakeRValue(), referenceReceiverBuilder)
spilledReferenceReceiver = referenceReceiverBuilder.BuildSequenceAndFree(Me.F, spilledReferenceReceiver)
Dim referenceReceiverSpillSequence = TryCast(spilledReferenceReceiver, BoundSpillSequence)
If referenceReceiverSpillSequence IsNot Nothing Then
' If condition `(object)default(T) != null` is true at execution time,
' the T is a value type. And it is a reference type otherwise.
Dim isValueTypeCheck = Me.F.ReferenceIsNotNothing(Me.F.DirectCast(Me.F.DirectCast(Me.F.Null(), receiverType),
Me.F.SpecialType(SpecialType.System_Object)))
builder.AssumeFieldsIfNeeded(referenceReceiverSpillSequence)
builder.AddLocals(referenceReceiverSpillSequence.Locals)
builder.AddStatement(Me.F.If(Me.F.Not(isValueTypeCheck), Me.F.StatementList(referenceReceiverSpillSequence.Statements)))
spilledReferenceReceiver = referenceReceiverSpillSequence.ValueOpt
End If
receiverOpt = New BoundComplexConditionalAccessReceiver(receiverOpt.Syntax, receiverOpt, spilledReferenceReceiver, receiverType)
End If
Else
receiverOpt = SpillLValue(receiverOpt, isReceiver:=True, evaluateSideEffects:=True, builder:=builder)
End If
End If
arguments = SpillExpressionList(builder, arguments)
End If
Return builder.BuildSequenceAndFree(Me.F,
rewritten.Update(rewritten.Method,
rewritten.MethodGroupOpt,
receiverOpt,
arguments,
rewritten.DefaultArguments,
rewritten.ConstantValueOpt,
isLValue:=rewritten.IsLValue,
suppressObjectClone:=rewritten.SuppressObjectClone,
type:=rewritten.Type))
End Function
Public Overrides Function VisitObjectCreationExpression(node As BoundObjectCreationExpression) As BoundNode
Dim rewritten = DirectCast(MyBase.VisitObjectCreationExpression(node), BoundObjectCreationExpression)
Dim arguments As ImmutableArray(Of BoundExpression) = rewritten.Arguments
Debug.Assert(rewritten.InitializerOpt Is Nothing)
If Not NeedsSpill(arguments) Then
Return rewritten
End If
Dim builder As New SpillBuilder()
arguments = SpillExpressionList(builder, arguments)
Return builder.BuildSequenceAndFree(Me.F,
rewritten.Update(rewritten.ConstructorOpt,
arguments,
rewritten.DefaultArguments,
rewritten.InitializerOpt,
rewritten.Type))
End Function
Public Overrides Function VisitDelegateCreationExpression(node As BoundDelegateCreationExpression) As BoundNode
Dim rewritten = DirectCast(MyBase.VisitDelegateCreationExpression(node), BoundDelegateCreationExpression)
Dim receiverOpt As BoundExpression = rewritten.ReceiverOpt
Debug.Assert(rewritten.RelaxationLambdaOpt Is Nothing)
Debug.Assert(rewritten.RelaxationReceiverPlaceholderOpt Is Nothing)
If Not NeedsSpill(receiverOpt) Then
Return rewritten
End If
Debug.Assert(receiverOpt.Kind = BoundKind.SpillSequence)
Dim spill = DirectCast(receiverOpt, BoundSpillSequence)
Return SpillSequenceWithNewValue(spill,
rewritten.Update(spill.ValueOpt,
rewritten.Method,
rewritten.RelaxationLambdaOpt,
rewritten.RelaxationReceiverPlaceholderOpt,
rewritten.MethodGroupOpt,
rewritten.Type))
End Function
Public Overrides Function VisitBinaryOperator(node As BoundBinaryOperator) As BoundNode
Dim rewritten = DirectCast(MyBase.VisitBinaryOperator(node), BoundBinaryOperator)
Dim left As BoundExpression = rewritten.Left
Dim right As BoundExpression = rewritten.Right
If Not NeedsSpill(left) AndAlso Not NeedsSpill(right) Then
Return rewritten
End If
Dim builder As New SpillBuilder()
Dim operatorKind As BinaryOperatorKind = rewritten.OperatorKind And BinaryOperatorKind.OpMask
Debug.Assert(operatorKind = (rewritten.OperatorKind And Not (BinaryOperatorKind.IsOperandOfConditionalBranch Or BinaryOperatorKind.OptimizableForConditionalBranch)))
If operatorKind = BinaryOperatorKind.AndAlso OrElse operatorKind = BinaryOperatorKind.OrElse Then
' NOTE: Short circuit operators need to evaluate the right optionally
Dim spilledLeft = SpillValue(left, builder)
Dim tempLocal As LocalSymbol = Me.F.SynthesizedLocal(rewritten.Type)
builder.AddLocal(tempLocal)
builder.AddStatement(
If(operatorKind = BinaryOperatorKind.AndAlso,
Me.F.If(condition:=spilledLeft,
thenClause:=MakeAssignmentStatement(right, tempLocal, builder),
elseClause:=MakeAssignmentStatement(Me.F.Literal(False), tempLocal)),
Me.F.If(condition:=spilledLeft,
thenClause:=MakeAssignmentStatement(Me.F.Literal(True), tempLocal),
elseClause:=MakeAssignmentStatement(right, tempLocal, builder))))
Return builder.BuildSequenceAndFree(Me.F,
Me.F.Local(tempLocal, False))
Else
' Regular binary operator
Dim newArgs As ImmutableArray(Of BoundExpression) = SpillExpressionList(builder, left, right)
Return builder.BuildSequenceAndFree(Me.F,
rewritten.Update(rewritten.OperatorKind,
newArgs(0),
newArgs(1),
rewritten.Checked,
rewritten.ConstantValueOpt,
rewritten.Type))
End If
End Function
Public Overrides Function VisitAssignmentOperator(node As BoundAssignmentOperator) As BoundNode
Return ProcessRewrittenAssignmentOperator(DirectCast(MyBase.VisitAssignmentOperator(node), BoundAssignmentOperator))
End Function
Friend Function ProcessRewrittenAssignmentOperator(rewritten As BoundAssignmentOperator) As BoundExpression
Debug.Assert(rewritten.LeftOnTheRightOpt Is Nothing)
Dim left As BoundExpression = rewritten.Left
Dim right As BoundExpression = rewritten.Right
Dim leftRequiresSpill As Boolean = NeedsSpill(left)
Dim rightRequiresSpill As Boolean = NeedsSpill(right)
If Not leftRequiresSpill AndAlso Not rightRequiresSpill Then
Return rewritten
End If
If Not rightRequiresSpill Then
' only lvalue contains await
' Spill(l_sideEffects, lvalue) = rvalue
' is rewritten as:
' Spill(l_sideEffects, lvalue = rvalue)
Debug.Assert(left.Kind = BoundKind.SpillSequence)
Dim spillSequence = DirectCast(left, BoundSpillSequence)
Return SpillSequenceWithNewValue(spillSequence,
rewritten.Update(spillSequence.ValueOpt,
rewritten.LeftOnTheRightOpt,
right,
rewritten.SuppressObjectClone,
rewritten.Type))
End If
Dim builder As New SpillBuilder()
Debug.Assert(left.IsLValue)
Dim spilledLeft As BoundExpression = SpillLValue(left, isReceiver:=False, evaluateSideEffects:=True, builder:=builder, isAssignmentTarget:=True)
Dim rightAsSpillSequence = DirectCast(right, BoundSpillSequence)
builder.AddSpill(rightAsSpillSequence)
Return builder.BuildSequenceAndFree(Me.F,
rewritten.Update(spilledLeft,
rewritten.LeftOnTheRightOpt,
rightAsSpillSequence.ValueOpt,
rewritten.SuppressObjectClone,
rewritten.Type))
End Function
Public Overrides Function VisitReferenceAssignment(node As BoundReferenceAssignment) As BoundNode
Dim origByRefLocal As BoundLocal = node.ByRefLocal
Dim local As LocalSymbol = origByRefLocal.LocalSymbol
Dim rewrittenType As TypeSymbol = VisitType(node.Type)
If Not Me.Proxies.ContainsKey(local) Then
' ByRef local was not captured
Dim rewrittenLeft As BoundLocal = DirectCast(Me.VisitExpression(origByRefLocal), BoundLocal)
Dim rewrittenRight As BoundExpression = Me.VisitExpression(node.LValue)
Debug.Assert(rewrittenRight.IsLValue)
Dim rightRequiresSpill As Boolean = NeedsSpill(rewrittenRight)
If Not rightRequiresSpill Then
Return node.Update(rewrittenLeft, rewrittenRight, node.IsLValue, rewrittenType)
Else
' The right is spilled, but the left is still a ByRef local
' Rewrite
' ReferenceAssignment(<by-ref-local> = Spill( ..., <l-value> ) )
' Into
' Spill( ..., ReferenceAssignment( <by-ref-local>, <l-value> ) )
Debug.Assert(rewrittenRight.Kind = BoundKind.SpillSequence)
Dim rightSpill = DirectCast(rewrittenRight, BoundSpillSequence)
Return SpillSequenceWithNewValue(rightSpill,
node.Update(rewrittenLeft,
rightSpill.ValueOpt,
node.IsLValue,
rewrittenType))
End If
End If
' Here we have an assignment expression that is initializing a ref
' local variable, and the ref local variable is to be lifted.
Dim capturedLocal As CapturedSymbolOrExpression = Me.Proxies(local)
' This builder will collect initializers for the captured local,
' these initializers are supposed to make sure all the parts of
' the capture are properly assigned
Dim initializersBuilder = ArrayBuilder(Of BoundExpression).GetInstance()
capturedLocal.CreateCaptureInitializationCode(Me, initializersBuilder)
Dim materializedCapture As BoundExpression = capturedLocal.Materialize(Me, node.IsLValue)
' Now... we actually do not need to rewrite or process 'right' in any way
' because we must have captured its essential parts in the local's capture.
' So we just create a sequence with these parts
If initializersBuilder.Count = 0 Then
initializersBuilder.Free()
Return materializedCapture
End If
initializersBuilder.Add(materializedCapture)
Return Me.F.Sequence(initializersBuilder.ToArrayAndFree)
End Function
Public Overrides Function VisitFieldAccess(node As BoundFieldAccess) As BoundNode
Dim rewritten = DirectCast(MyBase.VisitFieldAccess(node), BoundFieldAccess)
Dim receiverOpt As BoundExpression = rewritten.ReceiverOpt
If Not NeedsSpill(receiverOpt) Then
Return rewritten
End If
Debug.Assert(receiverOpt.Kind = BoundKind.SpillSequence)
Dim spillSequence = DirectCast(receiverOpt, BoundSpillSequence)
Return SpillSequenceWithNewValue(spillSequence,
rewritten.Update(spillSequence.ValueOpt,
rewritten.FieldSymbol,
rewritten.IsLValue,
rewritten.SuppressVirtualCalls,
constantsInProgressOpt:=Nothing,
rewritten.Type))
End Function
Public Overrides Function VisitDirectCast(node As BoundDirectCast) As BoundNode
Dim rewritten = DirectCast(MyBase.VisitDirectCast(node), BoundDirectCast)
Dim operand As BoundExpression = rewritten.Operand
Debug.Assert(rewritten.RelaxationLambdaOpt Is Nothing)
If Not NeedsSpill(operand) Then
Return rewritten
End If
Debug.Assert(operand.Kind = BoundKind.SpillSequence)
Dim spillSequence = DirectCast(operand, BoundSpillSequence)
Return SpillSequenceWithNewValue(spillSequence,
rewritten.Update(spillSequence.ValueOpt,
rewritten.ConversionKind,
rewritten.SuppressVirtualCalls,
rewritten.ConstantValueOpt,
rewritten.RelaxationLambdaOpt,
rewritten.Type))
End Function
Public Overrides Function VisitTryCast(node As BoundTryCast) As BoundNode
Dim rewritten = DirectCast(MyBase.VisitTryCast(node), BoundTryCast)
Dim operand As BoundExpression = rewritten.Operand
Debug.Assert(rewritten.RelaxationLambdaOpt Is Nothing)
If Not NeedsSpill(operand) Then
Return rewritten
End If
Debug.Assert(operand.Kind = BoundKind.SpillSequence)
Dim spillSequence = DirectCast(operand, BoundSpillSequence)
Return SpillSequenceWithNewValue(spillSequence,
rewritten.Update(spillSequence.ValueOpt,
rewritten.ConversionKind,
rewritten.ConstantValueOpt,
rewritten.RelaxationLambdaOpt,
rewritten.Type))
End Function
Public Overrides Function VisitConversion(node As BoundConversion) As BoundNode
Dim rewritten = DirectCast(MyBase.VisitConversion(node), BoundConversion)
Dim operand As BoundExpression = rewritten.Operand
Debug.Assert(rewritten.ExtendedInfoOpt Is Nothing)
If Not NeedsSpill(operand) Then
Return rewritten
End If
Debug.Assert(operand.Kind = BoundKind.SpillSequence)
Dim spillSequence = DirectCast(operand, BoundSpillSequence)
Return SpillSequenceWithNewValue(spillSequence,
rewritten.Update(spillSequence.ValueOpt,
rewritten.ConversionKind,
rewritten.Checked,
rewritten.ExplicitCastInCode,
rewritten.ConstantValueOpt,
rewritten.ExtendedInfoOpt,
rewritten.Type))
End Function
Public Overrides Function VisitLValueToRValueWrapper(node As BoundLValueToRValueWrapper) As BoundNode
Dim rewritten = DirectCast(MyBase.VisitLValueToRValueWrapper(node), BoundLValueToRValueWrapper)
Debug.Assert(Not rewritten.IsLValue)
Dim operand As BoundExpression = rewritten.UnderlyingLValue
Debug.Assert(operand.IsLValue)
If Not NeedsSpill(operand) Then
Return rewritten
End If
Debug.Assert(operand.Kind = BoundKind.SpillSequence)
Dim spillSequence = DirectCast(operand, BoundSpillSequence)
Return SpillSequenceWithNewValue(spillSequence,
rewritten.Update(spillSequence.ValueOpt,
rewritten.Type))
End Function
Public Overrides Function VisitTernaryConditionalExpression(node As BoundTernaryConditionalExpression) As BoundNode
Dim rewritten = DirectCast(MyBase.VisitTernaryConditionalExpression(node), BoundTernaryConditionalExpression)
Dim condition As BoundExpression = rewritten.Condition
Dim whenTrue As BoundExpression = rewritten.WhenTrue
Dim whenFalse As BoundExpression = rewritten.WhenFalse
Dim conditionRequiresSpill As Boolean = NeedsSpill(condition)
If Not conditionRequiresSpill AndAlso Not NeedsSpill(whenTrue) AndAlso Not NeedsSpill(whenFalse) Then
Return rewritten
End If
Dim builder As New SpillBuilder()
If conditionRequiresSpill Then
Debug.Assert(condition.Kind = BoundKind.SpillSequence)
condition = SpillRValue(condition, builder)
End If
Dim sequenceValueOpt As BoundExpression
If Not rewritten.Type.IsVoidType() Then
Dim tempLocal As LocalSymbol = Me.F.SynthesizedLocal(rewritten.Type)
builder.AddLocal(tempLocal)
builder.AddStatement(
Me.F.If(
condition:=condition,
thenClause:=MakeAssignmentStatement(whenTrue, tempLocal, builder),
elseClause:=MakeAssignmentStatement(whenFalse, tempLocal, builder)))
sequenceValueOpt = Me.F.Local(tempLocal, False)
Else
builder.AddStatement(
Me.F.If(
condition:=condition,
thenClause:=MakeExpressionStatement(whenTrue, builder),
elseClause:=MakeExpressionStatement(whenFalse, builder)))
sequenceValueOpt = Nothing
End If
Return builder.BuildSequenceAndFree(Me.F, sequenceValueOpt)
End Function
Private Function MakeAssignmentStatement(expression As BoundExpression, temp As LocalSymbol, <[In], Out> ByRef builder As SpillBuilder) As BoundStatement
If NeedsSpill(expression) Then
Debug.Assert(expression.Kind = BoundKind.SpillSequence)
Dim spill = DirectCast(expression, BoundSpillSequence)
builder.AssumeFieldsIfNeeded(spill)
Return RewriteSpillSequenceIntoBlock(spill, False, Me.F.Assignment(Me.F.Local(temp, True), spill.ValueOpt))
Else
Return Me.F.Assignment(Me.F.Local(temp, True), expression)
End If
End Function
Private Function MakeAssignmentStatement(expression As BoundExpression, temp As LocalSymbol) As BoundStatement
Debug.Assert(Not NeedsSpill(expression))
Return Me.F.Assignment(Me.F.Local(temp, True), expression)
End Function
Private Class ConditionalAccessReceiverPlaceholderReplacementInfo
Public ReadOnly PlaceholderId As Integer
Public IsSpilled As Boolean
Public ForceCaptureIfReferenceType As Boolean
Public Sub New(placeholderId As Integer)
Me.PlaceholderId = placeholderId
Me.IsSpilled = False
Me.ForceCaptureIfReferenceType = False
End Sub
End Class
Private _conditionalAccessReceiverPlaceholderReplacementInfo As ConditionalAccessReceiverPlaceholderReplacementInfo = Nothing
Public Overrides Function VisitLoweredConditionalAccess(node As BoundLoweredConditionalAccess) As BoundNode
Dim type As TypeSymbol = Me.VisitType(node.Type)
Dim receiverOrCondition As BoundExpression = DirectCast(Me.Visit(node.ReceiverOrCondition), BoundExpression)
Dim receiverOrConditionNeedsSpill = NeedsSpill(receiverOrCondition)
Dim saveConditionalAccessReceiverPlaceholderReplacementInfo = _conditionalAccessReceiverPlaceholderReplacementInfo
Dim conditionalAccessReceiverPlaceholderReplacementInfo As ConditionalAccessReceiverPlaceholderReplacementInfo
If node.PlaceholderId <> 0 Then
conditionalAccessReceiverPlaceholderReplacementInfo = New ConditionalAccessReceiverPlaceholderReplacementInfo(node.PlaceholderId)
Else
conditionalAccessReceiverPlaceholderReplacementInfo = Nothing
End If
_conditionalAccessReceiverPlaceholderReplacementInfo = conditionalAccessReceiverPlaceholderReplacementInfo
Dim whenNotNull As BoundExpression = DirectCast(Me.Visit(node.WhenNotNull), BoundExpression)
Dim whenNotNullNeedsSpill = NeedsSpill(whenNotNull)
Debug.Assert(conditionalAccessReceiverPlaceholderReplacementInfo Is Nothing OrElse
(Not conditionalAccessReceiverPlaceholderReplacementInfo.IsSpilled OrElse whenNotNullNeedsSpill))
_conditionalAccessReceiverPlaceholderReplacementInfo = Nothing
Dim whenNullOpt As BoundExpression = DirectCast(Me.Visit(node.WhenNullOpt), BoundExpression)
Dim whenNullNeedsSpill = If(whenNullOpt IsNot Nothing, NeedsSpill(whenNullOpt), False)
_conditionalAccessReceiverPlaceholderReplacementInfo = saveConditionalAccessReceiverPlaceholderReplacementInfo
If Not receiverOrConditionNeedsSpill AndAlso Not whenNotNullNeedsSpill AndAlso Not whenNullNeedsSpill Then
Return node.Update(receiverOrCondition,
node.CaptureReceiver,
node.PlaceholderId,
whenNotNull,
whenNullOpt,
type)
End If
If Not whenNotNullNeedsSpill AndAlso Not whenNullNeedsSpill Then
Debug.Assert(receiverOrConditionNeedsSpill)
Dim spill = DirectCast(receiverOrCondition, BoundSpillSequence)
Return SpillSequenceWithNewValue(spill, node.Update(spill.ValueOpt,
node.CaptureReceiver,
node.PlaceholderId,
whenNotNull,
whenNullOpt,
type))
End If
Dim builder As New SpillBuilder()
If receiverOrConditionNeedsSpill Then
Dim spill = DirectCast(receiverOrCondition, BoundSpillSequence)
builder.AddSpill(spill)
receiverOrCondition = spill.ValueOpt
End If
Dim condition As BoundExpression
If node.PlaceholderId = 0 Then
Debug.Assert(receiverOrCondition.Type.IsBooleanType())
condition = receiverOrCondition
Else
Debug.Assert(Not receiverOrCondition.Type.IsBooleanType())
Dim receiver As BoundExpression = receiverOrCondition
Dim nullCheckTarget As BoundExpression
Dim placeholderReplacement As BoundExpression
If node.CaptureReceiver OrElse conditionalAccessReceiverPlaceholderReplacementInfo.IsSpilled Then
' Let's spill or capture it then.
If node.CaptureReceiver OrElse conditionalAccessReceiverPlaceholderReplacementInfo.ForceCaptureIfReferenceType Then
' We need to capture the receiver by value.
Dim capturedReceiver As BoundExpression
' However, when receiver is not known to be a reference type, we will not be using captured value for a value type receiver.
' In that case, we should perform real spilling first in order to avoid evaluating the side-effects more than one time,
' to enforce order of evaluation and to decrease the size of the code that we will duplicate (1 - receiver evaluation during capture,
' 2 - value type receiver evaluation).
If Not receiver.Type.IsReferenceType Then
receiver = SpillValue(receiver, isReceiver:=True, evaluateSideEffects:=True, builder:=builder)
End If
' If receiver is not spilled, we can use a local to capture receiver's value. If receiver is spilled, use SpillRValue to accomplish this
' because values of locals are not preserved across awaits.
If conditionalAccessReceiverPlaceholderReplacementInfo.IsSpilled Then
If node.CaptureReceiver OrElse receiver.Type.IsReferenceType Then
nullCheckTarget = SpillRValue(receiver.MakeRValue(), builder:=builder)
capturedReceiver = nullCheckTarget
Else
Debug.Assert(conditionalAccessReceiverPlaceholderReplacementInfo.ForceCaptureIfReferenceType)
Dim referenceReceiverBuilder As New SpillBuilder()
Dim spilledReferenceReceiver As BoundExpression = SpillRValue(receiver.MakeRValue(), referenceReceiverBuilder)
spilledReferenceReceiver = referenceReceiverBuilder.BuildSequenceAndFree(Me.F, spilledReferenceReceiver)
Dim referenceReceiverSpillSequence = TryCast(spilledReferenceReceiver, BoundSpillSequence)
If referenceReceiverSpillSequence IsNot Nothing Then
' If condition `(object)default(T) != null` is true at execution time,
' the T is a value type. And it is a reference type otherwise.
Dim isValueTypeCheck = Me.F.ReferenceIsNotNothing(Me.F.DirectCast(Me.F.DirectCast(Me.F.Null(), receiver.Type),
Me.F.SpecialType(SpecialType.System_Object)))
builder.AssumeFieldsIfNeeded(referenceReceiverSpillSequence)
builder.AddLocals(referenceReceiverSpillSequence.Locals)
builder.AddStatement(Me.F.If(Me.F.Not(isValueTypeCheck), Me.F.StatementList(referenceReceiverSpillSequence.Statements)))
spilledReferenceReceiver = referenceReceiverSpillSequence.ValueOpt
End If
capturedReceiver = spilledReferenceReceiver
nullCheckTarget = New BoundComplexConditionalAccessReceiver(Me.F.Syntax,
receiver,
capturedReceiver,
receiver.Type)
End If
Else
Dim receiverLocal As LocalSymbol = Nothing
receiverLocal = Me.F.SynthesizedLocal(receiver.Type)
builder.AddLocal(receiverLocal)
nullCheckTarget = Me.F.AssignmentExpression(Me.F.Local(receiverLocal, isLValue:=True), receiver.MakeRValue())
capturedReceiver = Me.F.Local(receiverLocal, isLValue:=True)
End If
If nullCheckTarget.Type.IsReferenceType Then
placeholderReplacement = capturedReceiver
Else
Debug.Assert(Not nullCheckTarget.Type.IsValueType)
Debug.Assert(nullCheckTarget.Type.IsTypeParameter())
' Note, as part of "receiver IsNot Nothing" check below, we are capturing receiver only for reference types.
' When this happens, in order to avoid a race, we shouldn't reevaluate the receiver again, the capture
' should be the target of the access. However, when receiver is a value type, in order to preserve
' side-effects, the original receiver should be the target of the access. That is why we don't even
' capture it at run-time.
placeholderReplacement = New BoundComplexConditionalAccessReceiver(Me.F.Syntax,
receiver,
capturedReceiver,
receiver.Type)
End If
Else
Debug.Assert(conditionalAccessReceiverPlaceholderReplacementInfo.IsSpilled)
placeholderReplacement = SpillValue(receiver, isReceiver:=True, evaluateSideEffects:=True, builder:=builder)
nullCheckTarget = placeholderReplacement.MakeRValue()
End If
Else
placeholderReplacement = receiver
nullCheckTarget = placeholderReplacement.MakeRValue()
End If
' We need to revisit the whenNotNull expression to replace placeholder
Dim rewriter As New ConditionalAccessReceiverPlaceholderReplacement(node.PlaceholderId, placeholderReplacement, RecursionDepth)
whenNotNull = DirectCast(rewriter.Visit(whenNotNull), BoundExpression)
Debug.Assert(rewriter.Replaced)
' We need to a add a null check for the receiver
If nullCheckTarget.Type.IsReferenceType Then
condition = Me.F.ReferenceIsNotNothing(nullCheckTarget)
Else
Debug.Assert(Not nullCheckTarget.Type.IsValueType)
Debug.Assert(nullCheckTarget.Type.IsTypeParameter())
' The "receiver IsNot Nothing" check becomes
' Not <receiver's type is reference type> OrElse receiver IsNot Nothing
' The <receiver's type is reference type> is performed by boxing default value of receiver's type and checking if it is a null reference.
Dim notReferenceType = Me.F.ReferenceIsNotNothing(Me.F.DirectCast(Me.F.DirectCast(Me.F.Null(),
nullCheckTarget.Type),
Me.F.SpecialType(SpecialType.System_Object)))
condition = Me.F.LogicalOrElse(notReferenceType,
Me.F.ReferenceIsNotNothing(Me.F.DirectCast(nullCheckTarget,
Me.F.SpecialType(SpecialType.System_Object))))
End If
End If
If whenNullOpt Is Nothing Then
Debug.Assert(type.IsVoidType())
builder.AddStatement(
Me.F.If(condition:=condition,
thenClause:=MakeExpressionStatement(whenNotNull, builder)))
Return builder.BuildSequenceAndFree(Me.F, expression:=Nothing)
Else
Debug.Assert(Not type.IsVoidType())
Dim tempLocal As LocalSymbol = Me.F.SynthesizedLocal(type)
builder.AddLocal(tempLocal)
builder.AddStatement(Me.F.If(condition:=condition,
thenClause:=MakeAssignmentStatement(whenNotNull, tempLocal, builder),
elseClause:=MakeAssignmentStatement(whenNullOpt, tempLocal, builder)))
Return builder.BuildSequenceAndFree(Me.F, expression:=Me.F.Local(tempLocal, False))
End If
End Function
Private NotInheritable Class ConditionalAccessReceiverPlaceholderReplacement
Inherits BoundTreeRewriterWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
Private ReadOnly _placeholderId As Integer
Private ReadOnly _replaceWith As BoundExpression
Private _replaced As Boolean
Public Sub New(placeholderId As Integer, replaceWith As BoundExpression, recursionDepth As Integer)
MyBase.New(recursionDepth)
Me._placeholderId = placeholderId
Me._replaceWith = replaceWith
End Sub
Public ReadOnly Property Replaced As Boolean
Get
Return _replaced
End Get
End Property
Public Overrides Function VisitConditionalAccessReceiverPlaceholder(node As BoundConditionalAccessReceiverPlaceholder) As BoundNode
If _placeholderId = node.PlaceholderId Then
Debug.Assert(Not _replaced)
_replaced = True
Dim result = _replaceWith
If node.IsLValue Then
Debug.Assert(result.IsLValue)
Return result
Else
Return result.MakeRValue()
End If
End If
Return node
End Function
End Class
Public Overrides Function VisitConditionalAccessReceiverPlaceholder(node As BoundConditionalAccessReceiverPlaceholder) As BoundNode
If _conditionalAccessReceiverPlaceholderReplacementInfo Is Nothing OrElse _conditionalAccessReceiverPlaceholderReplacementInfo.PlaceholderId <> node.PlaceholderId Then
Throw ExceptionUtilities.Unreachable
End If
Return MyBase.VisitConditionalAccessReceiverPlaceholder(node)
End Function
Public Overrides Function VisitComplexConditionalAccessReceiver(node As BoundComplexConditionalAccessReceiver) As BoundNode
Dim result = DirectCast(MyBase.VisitComplexConditionalAccessReceiver(node), BoundComplexConditionalAccessReceiver)
If NeedsSpill(result.ValueTypeReceiver) OrElse NeedsSpill(result.ReferenceTypeReceiver) Then
Throw ExceptionUtilities.Unreachable
End If
Return result
End Function
Public Overrides Function VisitArrayCreation(node As BoundArrayCreation) As BoundNode
Debug.Assert(node.ArrayLiteralOpt Is Nothing)
Dim bounds As ImmutableArray(Of BoundExpression) = Me.VisitList(node.Bounds)
Dim rewrittenInitializer As BoundArrayInitialization = DirectCast(Me.Visit(node.InitializerOpt), BoundArrayInitialization)
Dim rewrittenType As TypeSymbol = Me.VisitType(node.Type)
Dim boundsRequiresSpill As Boolean = NeedsSpill(bounds)
Dim initRequiresSpill As Boolean = ArrayInitializerNeedsSpill(rewrittenInitializer)
If Not boundsRequiresSpill AndAlso Not initRequiresSpill Then
Debug.Assert(rewrittenInitializer Is Nothing OrElse rewrittenInitializer.Kind = BoundKind.ArrayInitialization)
Return node.Update(node.IsParamArrayArgument,
bounds,
DirectCast(rewrittenInitializer, BoundArrayInitialization),
Nothing,
Nothing,
rewrittenType)
End If
Dim builder As New SpillBuilder()
bounds = SpillExpressionList(builder, bounds)
If rewrittenInitializer IsNot Nothing Then
rewrittenInitializer = rewrittenInitializer.Update(
SpillExpressionList(builder, rewrittenInitializer.Initializers),
rewrittenInitializer.Type)
End If
Return builder.BuildSequenceAndFree(Me.F,
node.Update(node.IsParamArrayArgument,
bounds,
DirectCast(rewrittenInitializer, BoundArrayInitialization),
Nothing,
Nothing,
rewrittenType))
End Function
Private Function VisitArrayInitializationParts(node As BoundArrayInitialization) As BoundExpression
Debug.Assert(node IsNot Nothing)
Dim parts As ImmutableArray(Of BoundExpression) = node.Initializers
Dim partCount As Integer = parts.Length
Dim rewrittenParts(partCount - 1) As BoundExpression
For index = 0 To partCount - 1
Dim part As BoundExpression = parts(index)
rewrittenParts(index) = If(part.Kind = BoundKind.ArrayInitialization,
VisitArrayInitializationParts(
DirectCast(part, BoundArrayInitialization)),
VisitExpression(part))
Next
Dim rewrittenType As TypeSymbol = VisitType(node.Type)
Return node.Update(rewrittenParts.AsImmutableOrNull, rewrittenType)
End Function
Public Overrides Function VisitArrayInitialization(node As BoundArrayInitialization) As BoundNode
If node Is Nothing Then
Return Nothing
End If
Return VisitArrayInitializationParts(DirectCast(node, BoundArrayInitialization))
End Function
Public Overrides Function VisitArrayAccess(node As BoundArrayAccess) As BoundNode
Dim rewritten = DirectCast(MyBase.VisitArrayAccess(node), BoundArrayAccess)
Dim expression As BoundExpression = rewritten.Expression
Dim indices As ImmutableArray(Of BoundExpression) = rewritten.Indices
If Not NeedsSpill(expression) AndAlso Not NeedsSpill(indices) Then
Return rewritten
End If
Dim builder As New SpillBuilder()
Dim allExpressions = ImmutableArray.Create(Of BoundExpression)(expression).Concat(indices)
Dim allSpilledExpressions = SpillExpressionList(builder, allExpressions)
Return builder.BuildSequenceAndFree(Me.F,
rewritten.Update(expression:=allSpilledExpressions.First(),
indices:=allSpilledExpressions.RemoveAt(0),
rewritten.IsLValue,
rewritten.Type))
End Function
Public Overrides Function VisitArrayLength(node As BoundArrayLength) As BoundNode
Dim rewritten = DirectCast(MyBase.VisitArrayLength(node), BoundArrayLength)
Dim expression As BoundExpression = rewritten.Expression
If Not NeedsSpill(expression) Then
Return rewritten
End If
Debug.Assert(expression.Kind = BoundKind.SpillSequence)
Dim spill = DirectCast(expression, BoundSpillSequence)
Return SpillSequenceWithNewValue(spill,
rewritten.Update(spill.ValueOpt,
rewritten.Type))
End Function
Public Overrides Function VisitUnaryOperator(node As BoundUnaryOperator) As BoundNode
Dim rewritten = DirectCast(MyBase.VisitUnaryOperator(node), BoundUnaryOperator)
Dim operand As BoundExpression = rewritten.Operand
If Not NeedsSpill(operand) Then
Return rewritten
End If
Debug.Assert(operand.Kind = BoundKind.SpillSequence)
Dim spill = DirectCast(operand, BoundSpillSequence)
Return SpillSequenceWithNewValue(spill,
rewritten.Update(rewritten.OperatorKind,
spill.ValueOpt,
rewritten.Checked,
rewritten.ConstantValueOpt,
rewritten.Type))
End Function
Public Overrides Function VisitBinaryConditionalExpression(node As BoundBinaryConditionalExpression) As BoundNode
Dim rewritten = DirectCast(MyBase.VisitBinaryConditionalExpression(node), BoundBinaryConditionalExpression)
Debug.Assert(rewritten.ConvertedTestExpression Is Nothing)
Debug.Assert(rewritten.TestExpressionPlaceholder Is Nothing)
Dim testExpression As BoundExpression = rewritten.TestExpression
Dim elseExpression As BoundExpression = rewritten.ElseExpression
If Not NeedsSpill(testExpression) AndAlso Not NeedsSpill(elseExpression) Then
Return rewritten
End If
Dim builder As New SpillBuilder()
Dim tempLocal As LocalSymbol = Me.F.SynthesizedLocal(rewritten.Type)
builder.AddLocal(tempLocal)
builder.AddStatement(MakeAssignmentStatement(testExpression, tempLocal, builder))
builder.AddStatement(
Me.F.If(
condition:=Me.F.ReferenceIsNothing(Me.F.Local(tempLocal, False)),
thenClause:=MakeAssignmentStatement(elseExpression, tempLocal, builder)))
Return builder.BuildSequenceAndFree(Me.F,
Me.F.Local(tempLocal, False))
End Function
Public Overrides Function VisitTypeOf(node As BoundTypeOf) As BoundNode
Dim rewritten = DirectCast(MyBase.VisitTypeOf(node), BoundTypeOf)
Dim operand As BoundExpression = rewritten.Operand
If Not NeedsSpill(operand) Then
Return rewritten
End If
Debug.Assert(operand.Kind = BoundKind.SpillSequence)
Dim spill = DirectCast(operand, BoundSpillSequence)
Return SpillSequenceWithNewValue(spill,
rewritten.Update(spill.ValueOpt,
rewritten.IsTypeOfIsNotExpression,
rewritten.TargetType,
rewritten.Type))
End Function
Public Overrides Function VisitSequencePointExpression(node As BoundSequencePointExpression) As BoundNode
Dim rewritten = DirectCast(MyBase.VisitSequencePointExpression(node), BoundSequencePointExpression)
Dim expression As BoundExpression = rewritten.Expression
If Not NeedsSpill(expression) Then
Return rewritten
End If
Debug.Assert(expression.Kind = BoundKind.SpillSequence)
Dim spill = DirectCast(expression, BoundSpillSequence)
Return SpillSequenceWithNewValue(spill,
rewritten.Update(spill.ValueOpt,
rewritten.Type))
End Function
End Class
End Class
End Namespace
|