/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Lowering/LocalRewriter/LocalRewriter_StringConcat.vb
294 строки
15 KB
Julien Couvreur
Warn for multiple blank lines in source (#65451)
17 ноя 2022, 22:55
Не верифицирован
17 ноя 2022, 22:55
a8824e8
Код
Авторство
О чём код?
' 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 Class LocalRewriter ' The strategy of this rewrite is to do rewrite "locally". ' We analyze arguments of the concat in a shallow fashion assuming that ' lowering and optimizations (including this one) is already done for the arguments. ' Based on the arguments we select the most appropriate pattern for the current node. ' ' NOTE: it is not guaranteed that the node that we chose will be the most optimal since we have only ' local information - i.e. we look at the arguments, but we do not know about siblings. ' When we move to the parent, the node may be rewritten by this or some another optimization. ' ' Example: ' result = if( "abc" & "def" & Nothing , expr1 & "moo" & "baz" ) & expr2 ' ' Will rewrite into: ' result = Concat("abcdef", expr2) ' ' However there will be transient nodes like Concat(expr1 + "moo") that will not be present in the ' resulting tree. Private Function RewriteConcatenateOperator(node As BoundBinaryOperator) As BoundExpression Debug.Assert(node.Type.IsStringType() AndAlso node.Left.Type.IsStringType() AndAlso node.Right.Type.IsStringType(), "concat args should be strings here") Dim syntax = node.Syntax Dim loweredLeft = node.Left Dim loweredRight = node.Right Dim factory = New SyntheticBoundNodeFactory(Me._topMethod, Me._currentMethodOrLambda, syntax, Me._compilationState, Me._diagnostics) ' try fold two args without flattening. Dim folded As BoundExpression = TryFoldTwoConcatOperands(factory, loweredLeft, loweredRight) If folded IsNot Nothing Then Return folded End If ' flatten and merge - ( expr1 + "A" ) + ("B" + expr2) ===> (expr1 + "AB" + expr2) Dim leftFlattened = ArrayBuilder(Of BoundExpression).GetInstance() Dim rightFlattened = ArrayBuilder(Of BoundExpression).GetInstance() FlattenConcatArg(loweredLeft, leftFlattened) FlattenConcatArg(loweredRight, rightFlattened) If leftFlattened.Any AndAlso rightFlattened.Any Then folded = TryFoldTwoConcatOperands(factory, leftFlattened.Last(), rightFlattened.First()) If folded IsNot Nothing Then rightFlattened(0) = folded leftFlattened.RemoveLast() End If End If leftFlattened.AddRange(rightFlattened) rightFlattened.Free() Select Case leftFlattened.Count Case 0 leftFlattened.Free() Return factory.StringLiteral(ConstantValue.Create("")) Case 1 Dim result = leftFlattened(0) leftFlattened.Free() Return result Case 2 Dim left As BoundExpression = leftFlattened(0) Dim right As BoundExpression = leftFlattened(1) leftFlattened.Free() Return RewriteStringConcatenationTwoExprs(node, factory, left, right) Case 3 Dim first As BoundExpression = leftFlattened(0) Dim second As BoundExpression = leftFlattened(1) Dim third As BoundExpression = leftFlattened(2) leftFlattened.Free() Return RewriteStringConcatenationThreeExprs(node, factory, first, second, third) Case 4 Dim first As BoundExpression = leftFlattened(0) Dim second As BoundExpression = leftFlattened(1) Dim third As BoundExpression = leftFlattened(2) Dim fourth As BoundExpression = leftFlattened(3) leftFlattened.Free() Return RewriteStringConcatenationFourExprs(node, factory, first, second, third, fourth) Case Else Return RewriteStringConcatenationManyExprs(node, factory, leftFlattened.ToImmutableAndFree()) End Select End Function ''' <summary> ''' digs into known concat operators and unwraps their arguments ''' otherwise returns the expression as-is ''' ''' Generally we only need to recognize same node patterns that we create as a result of concatenation rewrite. ''' We could recognize some other nodes and unwrap to arguments ''' </summary> Private Sub FlattenConcatArg(lowered As BoundExpression, flattened As ArrayBuilder(Of BoundExpression)) Select Case lowered.Kind Case BoundKind.Call Dim boundCall As BoundCall = DirectCast(lowered, BoundCall) Dim method As MethodSymbol = boundCall.Method If method.IsShared AndAlso method.ContainingType.SpecialType = SpecialType.System_String Then If method Is Me.Compilation.GetSpecialTypeMember(SpecialMember.System_String__ConcatStringString) OrElse method Is Me.Compilation.GetSpecialTypeMember(SpecialMember.System_String__ConcatStringStringString) OrElse method Is Me.Compilation.GetSpecialTypeMember(SpecialMember.System_String__ConcatStringStringStringString) Then flattened.AddRange(boundCall.Arguments) Return End If If method Is Me.Compilation.GetSpecialTypeMember(SpecialMember.System_String__ConcatStringArray) Then Dim args As BoundArrayCreation = TryCast(boundCall.Arguments(0), BoundArrayCreation) If args IsNot Nothing Then Dim initializer As BoundArrayInitialization = args.InitializerOpt If initializer IsNot Nothing Then flattened.AddRange(initializer.Initializers) Return End If End If End If End If Case BoundKind.BinaryConditionalExpression Dim boundCoalesce = DirectCast(lowered, BoundBinaryConditionalExpression) If boundCoalesce.ConvertedTestExpression Is Nothing Then Dim elseExpr = boundCoalesce.ElseExpression If elseExpr.ConstantValueOpt IsNot Nothing AndAlso elseExpr.ConstantValueOpt.StringValue = "" Then flattened.AddRange(boundCoalesce.TestExpression) Return End If End If End Select ' fallback - if nothing above worked, leave arg as-is flattened.Add(lowered) End Sub ''' <summary> ''' folds two concat operands into one expression if possible ''' otherwise returns null ''' </summary> Private Function TryFoldTwoConcatOperands(factory As SyntheticBoundNodeFactory, loweredLeft As BoundExpression, loweredRight As BoundExpression) As BoundExpression Dim leftConst As ConstantValue = loweredLeft.ConstantValueOpt Dim rightConst As ConstantValue = loweredRight.ConstantValueOpt If leftConst IsNot Nothing AndAlso rightConst IsNot Nothing Then ' const concat may fail to fold if strings are huge. ' This would be unusual. Dim concatenated As ConstantValue = TryFoldTwoConcatConsts(leftConst, rightConst) If concatenated IsNot Nothing Then Return factory.StringLiteral(concatenated) End If End If If IsNullOrEmptyStringConstant(loweredLeft) Then If IsNullOrEmptyStringConstant(loweredRight) Then Return factory.Literal("") ElseIf Not _inExpressionLambda Then Return RewriteStringConcatenationOneExpr(factory, loweredRight) End If ElseIf Not _inExpressionLambda AndAlso IsNullOrEmptyStringConstant(loweredRight) Then Return RewriteStringConcatenationOneExpr(factory, loweredLeft) End If Return Nothing End Function Private Shared Function IsNullOrEmptyStringConstant(operand As BoundExpression) As Boolean Return (operand.ConstantValueOpt IsNot Nothing AndAlso String.IsNullOrEmpty(operand.ConstantValueOpt.StringValue)) OrElse operand.IsDefaultValueConstant End Function ''' <summary> ''' folds two concat constants into one if possible ''' otherwise returns null. ''' It is generally always possible to concat constants, unless resulting string would be too large. ''' </summary> Private Shared Function TryFoldTwoConcatConsts(leftConst As ConstantValue, rightConst As ConstantValue) As ConstantValue Dim leftVal As String = leftConst.StringValue Dim rightVal As String = rightConst.StringValue If Not leftConst.IsDefaultValue AndAlso Not rightConst.IsDefaultValue AndAlso leftVal.Length + rightVal.Length < 0 Then Return Nothing End If ' TODO: if transient string allocations are an issue, consider introducing constants that contain builders. ' it may be not so easy to even get here though, since typical ' "A" + "B" + "C" + ... cases should be folded in the binder as spec requires so. ' we would be mostly picking here edge cases like "A" + (object)null + "B" + (object)null + ... Return ConstantValue.Create(leftVal + rightVal) End Function ''' <summary> ''' Strangely enough there is such a thing as unary concatenation and it must be rewritten. ''' </summary> Private Shared Function RewriteStringConcatenationOneExpr(factory As SyntheticBoundNodeFactory, loweredOperand As BoundExpression) As BoundExpression Return factory.BinaryConditional(loweredOperand, factory.Literal("")) End Function Private Function RewriteStringConcatenationTwoExprs(node As BoundExpression, factory As SyntheticBoundNodeFactory, loweredLeft As BoundExpression, loweredRight As BoundExpression) As BoundExpression ' ' Call String.Concat(left, right) Const memberId As SpecialMember = SpecialMember.System_String__ConcatStringString Dim memberSymbol = DirectCast(GetSpecialTypeMember(memberId), MethodSymbol) If Not ReportMissingOrBadRuntimeHelper(node, memberId, memberSymbol) Then Return factory.Call(Nothing, memberSymbol, loweredLeft, loweredRight) End If Return node End Function Private Function RewriteStringConcatenationThreeExprs(node As BoundExpression, factory As SyntheticBoundNodeFactory, loweredFirst As BoundExpression, loweredSecond As BoundExpression, loweredThird As BoundExpression) As BoundExpression ' ' Call String.Concat(first, second, third) Const memberId As SpecialMember = SpecialMember.System_String__ConcatStringStringString Dim memberSymbol = DirectCast(GetSpecialTypeMember(memberId), MethodSymbol) If Not ReportMissingOrBadRuntimeHelper(node, memberId, memberSymbol) Then Return factory.Call(Nothing, memberSymbol, loweredFirst, loweredSecond, loweredThird) End If Return node End Function Private Function RewriteStringConcatenationFourExprs(node As BoundExpression, factory As SyntheticBoundNodeFactory, loweredFirst As BoundExpression, loweredSecond As BoundExpression, loweredThird As BoundExpression, loweredFourth As BoundExpression) As BoundExpression ' ' Call String.Concat(first, second, third) Const memberId As SpecialMember = SpecialMember.System_String__ConcatStringStringStringString Dim memberSymbol = DirectCast(GetSpecialTypeMember(memberId), MethodSymbol) If Not ReportMissingOrBadRuntimeHelper(node, memberId, memberSymbol) Then Return factory.Call(Nothing, memberSymbol, loweredFirst, loweredSecond, loweredThird, loweredFourth) End If Return node End Function Private Function RewriteStringConcatenationManyExprs(node As BoundExpression, factory As SyntheticBoundNodeFactory, loweredArgs As ImmutableArray(Of BoundExpression)) As BoundExpression Const memberId As SpecialMember = SpecialMember.System_String__ConcatStringArray Dim memberSymbol = DirectCast(GetSpecialTypeMember(memberId), MethodSymbol) If Not ReportMissingOrBadRuntimeHelper(node, memberId, memberSymbol) Then Dim argArray = factory.Array(node.Type, loweredArgs) Return factory.Call(Nothing, memberSymbol, ImmutableArray.Create(Of BoundExpression)(argArray)) End If Return node End Function End Class End Namespace