/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Lowering/WithExpressionRewriter.vb
373 строки
17 KB
AlekseyTs
Ensure proper receiver value is used for a constrained call invocation (#70401)
02 ноя 2023, 01:39
Не верифицирован
02 ноя 2023, 01:39
0d04411
Код
Авторство
О чём код?
' 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.Runtime.InteropServices Imports Microsoft.CodeAnalysis.Collections 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 Friend NotInheritable Class WithExpressionRewriter Private ReadOnly _withSyntax As WithStatementSyntax Public Structure Result Public Sub New(expression As BoundExpression, locals As ImmutableArray(Of LocalSymbol), initializers As ImmutableArray(Of BoundExpression), capturedLvalueByRefCallOrProperty As BoundExpression) Me.Expression = expression Me.Locals = locals Me.Initializers = initializers Me.CapturedLvalueByRefCallOrProperty = capturedLvalueByRefCallOrProperty End Sub ''' <summary> Expression to be used instead of With statement expression placeholder </summary> Public ReadOnly Expression As BoundExpression ''' <summary> Locals being used </summary> Public ReadOnly Locals As ImmutableArray(Of LocalSymbol) ''' <summary> Locals initialization expressions </summary> Public ReadOnly Initializers As ImmutableArray(Of BoundExpression) Public ReadOnly CapturedLvalueByRefCallOrProperty As BoundExpression End Structure Friend Sub New(withSyntax As WithStatementSyntax) Me._withSyntax = withSyntax End Sub #Region "State" Private Class State Public ReadOnly ContainingMember As Symbol Public ReadOnly DoNotUseByRefLocal As Boolean Public ReadOnly Binder As Binder Public ReadOnly PreserveIdentityOfLValues As Boolean Public ReadOnly IsDraftRewrite As Boolean Private _locals As ArrayBuilder(Of LocalSymbol) = Nothing Private _initializers As ArrayBuilder(Of BoundExpression) = Nothing Public _capturedLvalueByRefCallOrProperty As BoundExpression = Nothing Public Sub New(containingMember As Symbol, doNotUseByRefLocal As Boolean, binder As Binder, preserveIdentityOfLValues As Boolean, isDraftRewrite As Boolean) Me.ContainingMember = containingMember Me.DoNotUseByRefLocal = doNotUseByRefLocal Me.Binder = binder Me.PreserveIdentityOfLValues = preserveIdentityOfLValues Me.IsDraftRewrite = isDraftRewrite End Sub Public Sub AddLocal(local As LocalSymbol, initializer As BoundExpression) Debug.Assert(local IsNot Nothing) Debug.Assert(initializer IsNot Nothing) If Me._locals Is Nothing Then Debug.Assert(Me._initializers Is Nothing) Me._locals = ArrayBuilder(Of LocalSymbol).GetInstance() Me._initializers = ArrayBuilder(Of BoundExpression).GetInstance() End If Me._locals.Add(local) Me._initializers.Add(initializer) End Sub Public Function CreateResult(expression As BoundExpression) As Result Return New Result(expression, If(Me._locals Is Nothing, ImmutableArray(Of LocalSymbol).Empty, Me._locals.ToImmutableAndFree()), If(Me._initializers Is Nothing, ImmutableArray(Of BoundExpression).Empty, Me._initializers.ToImmutableAndFree()), Me._capturedLvalueByRefCallOrProperty) End Function End Class #End Region #Region "Implementation" Private Function CaptureInATemp(value As BoundExpression, state As State) As BoundLocal Dim type As TypeSymbol = value.Type Debug.Assert(type IsNot Nothing AndAlso Not type.IsVoidType()) Dim local As New SynthesizedLocal(state.ContainingMember, type, SynthesizedLocalKind.With, _withSyntax) Dim boundLocal = New BoundLocal(value.Syntax, local, isLValue:=True, type:=type).MakeCompilerGenerated() If value.IsLValue Then ' Region analysis depends on node identity, let's preserve it by using a wrapper. If state.PreserveIdentityOfLValues Then value = New BoundLValueToRValueWrapper(value.Syntax, value, value.Type).MakeCompilerGenerated() Else value = value.MakeRValue() End If End If state.AddLocal(local, New BoundAssignmentOperator( value.Syntax, boundLocal, value, suppressObjectClone:=True, type:=type).MakeCompilerGenerated()) Return boundLocal End Function Private Function CaptureInAByRefTemp(value As BoundExpression, state As State) As BoundExpression Debug.Assert(value.IsLValue()) Dim type As TypeSymbol = value.Type Debug.Assert(type IsNot Nothing AndAlso Not type.IsVoidType()) Dim local As New SynthesizedLocal(state.ContainingMember, type, SynthesizedLocalKind.With, _withSyntax, isByRef:=True) Dim boundLocal = New BoundLocal(value.Syntax, local, isLValue:=True, type:=type).MakeCompilerGenerated() state.AddLocal(local, New BoundReferenceAssignment( value.Syntax, boundLocal, value, True, type:=type).MakeCompilerGenerated()) Return boundLocal End Function Private Function CaptureArrayAccess(value As BoundArrayAccess, state As State) As BoundExpression Debug.Assert(value.IsLValue) Dim boundArrayTemp As BoundExpression = CaptureInATemp(value.Expression, state).MakeRValue() Dim n = value.Indices.Length Dim indices(n - 1) As BoundExpression For i = 0 To n - 1 indices(i) = CaptureRValue(value.Indices(i), state) Next Return value.Update(boundArrayTemp, indices.AsImmutableOrNull(), value.IsLValue, value.Type) End Function Private Function CaptureRValue(value As BoundExpression, state As State) As BoundExpression Dim kind As BoundKind = value.Kind Select Case kind Case BoundKind.BadVariable, BoundKind.Literal, BoundKind.MeReference, BoundKind.MyClassReference, BoundKind.MyBaseReference Return value End Select If value.IsValue AndAlso value.Type IsNot Nothing AndAlso Not value.Type.IsVoidType() Then Debug.Assert(Not value.IsLValue) Dim constantValue As ConstantValue = value.ConstantValueOpt If constantValue IsNot Nothing Then Debug.Assert(value.Kind <> BoundKind.Literal) Return value End If ' TODO: Might need to do some optimization for compiler generated locals. ' For example, no reason to recapture a local that is already a capture. Return CaptureInATemp(value, state).MakeRValue() End If Throw ExceptionUtilities.Unreachable End Function Private Function CaptureFieldAccess(value As BoundFieldAccess, state As State) As BoundExpression Debug.Assert(value.IsLValue) Dim fieldSymbol = value.FieldSymbol If fieldSymbol.IsShared AndAlso value.ReceiverOpt IsNot Nothing Then Return value.Update(Nothing, fieldSymbol, value.IsLValue, value.SuppressVirtualCalls, value.ConstantsInProgressOpt, value.Type) ElseIf value.ReceiverOpt Is Nothing Then Return value Else Dim receiver As BoundExpression = CaptureReceiver(value.ReceiverOpt, state) Return value.Update(receiver, fieldSymbol, value.IsLValue, value.SuppressVirtualCalls, value.ConstantsInProgressOpt, value.Type) End If End Function Private Function CaptureReceiver(value As BoundExpression, state As State) As BoundExpression Debug.Assert(value IsNot Nothing) If value.IsLValue AndAlso value.Type.IsReferenceType Then Return CaptureInATemp(value, state) Else Return CaptureExpression(value, state) End If End Function Private Function CaptureExpression(value As BoundExpression, state As State) As BoundExpression If Not value.IsLValue Then Return CaptureRValue(value, state) End If Select Case value.Kind Case BoundKind.ArrayAccess Return CaptureArrayAccess(DirectCast(value, BoundArrayAccess), state) Case BoundKind.FieldAccess Return CaptureFieldAccess(DirectCast(value, BoundFieldAccess), state) Case BoundKind.Local, BoundKind.Parameter Return value Case BoundKind.WithLValueExpressionPlaceholder ' NOTE: this may only happen in case of calling this rewriter from initial ' binding, in lowering phase all placeholders must already be substituted ' We need to replace the placeholder with 'draft substitute' to make sure ' we properly analyze it in flow analysis. This is important, for example, ' if we pass the original value typed local to substitute like in the following ' example: ' ' Dim s As New StructureType(...) ' With s ' .Field1 = 1 ' ... ' ' Note that we cannot just replace the placeholder with the 'original' expression ' because we should predict and use the expression which will actually be used ' in lowering, for example in the following scenario: ' ' With {expr} ' With .Field ' ... ' ' {expr} is of reference type we don't want to replace the placeholder with the ' outer expression, because otherwise it will get to initializers of the nested ' With statement and will take part in flow analysis ' ' Try and get the substitute from the binder, or leave placeholder 'as-is' to ' be replaced with proper substitute by flow analysis code when needed Dim substitute As BoundExpression = state.Binder.GetWithStatementPlaceholderSubstitute(DirectCast(value, BoundValuePlaceholderBase)) Return If(substitute IsNot Nothing, CaptureExpression(substitute, state), value) Case Else Return CaptureRValue(value, state) End Select End Function #End Region ''' <summary> ''' Given an expression specified for With statement produces: ''' 1) Expression - an expression to be used instead of expression placeholder ''' 2) Locals - a set of locals used to capture parts of Expression ''' 3) Initializers - initializers for Locals ''' ''' To be used in With statement only! ''' </summary> Public Function AnalyzeWithExpression( containingMember As Symbol, value As BoundExpression, doNotUseByRefLocal As Boolean, isDraftRewrite As Boolean, binder As Binder, Optional preserveIdentityOfLValues As Boolean = False ) As Result Dim state As New State(containingMember, doNotUseByRefLocal, binder, preserveIdentityOfLValues, isDraftRewrite) Return state.CreateResult(CaptureWithExpression(value, state)) End Function Private Function CaptureWithExpression(value As BoundExpression, state As State) As BoundExpression Dim type As TypeSymbol = value.Type Debug.Assert(type IsNot Nothing) Dim kind = value.Kind If kind = BoundKind.MeReference OrElse kind = BoundKind.MyClassReference OrElse kind = BoundKind.MyBaseReference Then ' Me reference can simply be reused instead of placeholder ' NOTE: MyClass & MyBase references may only get here in erroneous scenarios Return value End If If type.IsReferenceType Then ' Expressions of reference type are to be captured using a simple local Dim result As BoundLocal = CaptureInATemp(value, state) If Not value.IsLValue Then result = result.MakeRValue() End If Return result End If ' NOTE: Only structures and type parameters should reach this point Debug.Assert(value.Type.IsStructureType OrElse value.Type.IsTypeParameter) If Not value.IsLValue() Then ' All R-value value typed and type parameter typed ' expressions should be captured in simple locals Return CaptureInATemp(value, state).MakeRValue() End If ' NOTE: Only L-value expressions of value type or type parameter type If kind = BoundKind.Local OrElse kind = BoundKind.Parameter Then ' Locals and parameters of value or type parameter type can ' simply be reused instead of placeholder Debug.Assert(type.IsValueType OrElse type.IsTypeParameter) Return value End If ' If the expression is to be captured in lambda do not capture in a ref local. ' If the expression is a generic array element, getting a writable reference may fail ' and readonly reference cannot be stored in a temp, so do not capture in a ref. If Not (state.DoNotUseByRefLocal OrElse (value.Kind = BoundKind.ArrayAccess AndAlso value.Type.Kind = SymbolKind.TypeParameter)) Then Return CaptureInAByRefTemp(value, state) End If ' Otherwise, we need to capture parts of the expression in a set of non-ByRef locals Dim expression As BoundExpression = Nothing Select Case value.Kind Case BoundKind.ArrayAccess expression = CaptureArrayAccess(DirectCast(value, BoundArrayAccess), state) Case BoundKind.FieldAccess expression = CaptureFieldAccess(DirectCast(value, BoundFieldAccess), state) Case BoundKind.PropertyAccess If Not state.IsDraftRewrite OrElse Not DirectCast(value, BoundPropertyAccess).PropertySymbol.ReturnsByRef Then Throw ExceptionUtilities.UnexpectedValue(value.Kind) End If Debug.Assert(state.DoNotUseByRefLocal) If state._capturedLvalueByRefCallOrProperty Is Nothing Then state._capturedLvalueByRefCallOrProperty = value End If expression = CaptureInATemp(value, state) ' Capture by value for the purpose of draft rewrite Case BoundKind.Call If Not state.IsDraftRewrite OrElse Not DirectCast(value, BoundCall).Method.ReturnsByRef Then Throw ExceptionUtilities.UnexpectedValue(value.Kind) End If Debug.Assert(state.DoNotUseByRefLocal) If state._capturedLvalueByRefCallOrProperty Is Nothing Then state._capturedLvalueByRefCallOrProperty = value End If expression = CaptureInATemp(value, state) ' Capture by value for the purpose of draft rewrite Case Else Throw ExceptionUtilities.UnexpectedValue(value.Kind) End Select ' LValue-ness of expressions must be preserved Debug.Assert(expression IsNot Nothing) Debug.Assert(expression.IsLValue) Return expression End Function End Class End Namespace