/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Lowering/LocalRewriter/LocalRewriter_Call.vb
387 строк
19 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.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 NotInheritable Class LocalRewriter Public Overrides Function VisitCall(node As BoundCall) As BoundNode Debug.Assert(Not node.IsConstant, "Constant calls should become literals by now") Dim receiver As BoundExpression = node.ReceiverOpt Dim method As MethodSymbol = node.Method Dim arguments As ImmutableArray(Of BoundExpression) = node.Arguments ' Replace a call to AscW(<non constant char>) with a conversion, this makes sure we don't have a recursion inside AscW(Char). If method Is Compilation.GetWellKnownTypeMember(WellKnownMember.Microsoft_VisualBasic_Strings__AscWCharInt32) Then Return New BoundConversion(node.Syntax, VisitExpressionNode(arguments(0)), ConversionKind.WideningNumeric, checked:=False, explicitCastInCode:=True, type:=node.Type) End If ' Code below is for remapping of versioned functions. ' ' Code compiled with VS7.1 or earlier must run on VS8.0 with no behavior changes. ' However, since VS8.0 introduces several new features which change the behavior of ' Public functions, we introduce the new behavior in a new function and map references ' to the old function onto the new function. In this way, old code continues to run and ' new code gets the new behavior, but the Public function doesn't change at all, from the ' user's point of view. ' Example: ' ' Given: ' 1. User code snippet: "If IsNumeric(something) Then ..." ' 2. Public Function IsNumeric(o), has VS7.1 behavior. ' 3. Public Function Versioned.IsNumeric(o), has VS8.0 behavior. ' ' When compiled in VS7.1, the compiler binds to IsNumeric(o). ' When compiled in VS8.0, the compiler first binds to IsNumeric(o) and then remaps ' this binding to Versioned.IsNumeric(o) in the code generator so that the VS8.0 ' behavior is selected. ' Dim remappedMethodId As WellKnownMember = WellKnownMember.Count If method Is Compilation.GetWellKnownTypeMember(WellKnownMember.Microsoft_VisualBasic_Interaction__CallByName) Then remappedMethodId = WellKnownMember.Microsoft_VisualBasic_CompilerServices_Versioned__CallByName ElseIf method.ContainingSymbol Is Compilation.GetWellKnownType(WellKnownType.Microsoft_VisualBasic_Information) Then If method Is Compilation.GetWellKnownTypeMember(WellKnownMember.Microsoft_VisualBasic_Information__IsNumeric) Then remappedMethodId = WellKnownMember.Microsoft_VisualBasic_CompilerServices_Versioned__IsNumeric ElseIf method Is Compilation.GetWellKnownTypeMember(WellKnownMember.Microsoft_VisualBasic_Information__SystemTypeName) Then remappedMethodId = WellKnownMember.Microsoft_VisualBasic_CompilerServices_Versioned__SystemTypeName ElseIf method Is Compilation.GetWellKnownTypeMember(WellKnownMember.Microsoft_VisualBasic_Information__TypeName) Then remappedMethodId = WellKnownMember.Microsoft_VisualBasic_CompilerServices_Versioned__TypeName ElseIf method Is Compilation.GetWellKnownTypeMember(WellKnownMember.Microsoft_VisualBasic_Information__VbTypeName) Then remappedMethodId = WellKnownMember.Microsoft_VisualBasic_CompilerServices_Versioned__VbTypeName End If End If If remappedMethodId <> WellKnownMember.Count Then Dim remappedMethod = DirectCast(Compilation.GetWellKnownTypeMember(remappedMethodId), MethodSymbol) If remappedMethod IsNot Nothing AndAlso Not ReportMissingOrBadRuntimeHelper(node, remappedMethodId, remappedMethod) Then method = remappedMethod End If End If UpdateMethodAndArgumentsIfReducedFromMethod(method, receiver, arguments) Dim temporaries As ImmutableArray(Of SynthesizedLocal) = Nothing Dim copyBack As ImmutableArray(Of BoundExpression) = Nothing Dim suppressObjectClone As Boolean = node.SuppressObjectClone OrElse method Is Compilation.GetWellKnownTypeMember( WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__GetObjectValueObject) receiver = VisitExpressionNode(receiver) node = node.Update(method, Nothing, receiver, RewriteCallArguments(arguments, method.Parameters, temporaries, copyBack, suppressObjectClone), node.DefaultArguments, Nothing, isLValue:=node.IsLValue, suppressObjectClone:=True, type:=node.Type) If Not copyBack.IsDefault Then Return GenerateSequenceValueSideEffects(_currentMethodOrLambda, node, StaticCast(Of LocalSymbol).From(temporaries), copyBack) End If If Not temporaries.IsDefault Then If method.IsSub Then Return New BoundSequence(node.Syntax, StaticCast(Of LocalSymbol).From(temporaries), ImmutableArray.Create(Of BoundExpression)(node), Nothing, node.Type) Else Return New BoundSequence(node.Syntax, StaticCast(Of LocalSymbol).From(temporaries), ImmutableArray(Of BoundExpression).Empty, node, node.Type) End If End If Return node End Function Private Shared Sub UpdateMethodAndArgumentsIfReducedFromMethod( ByRef method As MethodSymbol, ByRef receiver As BoundExpression, ByRef arguments As ImmutableArray(Of BoundExpression)) If receiver Is Nothing Then Return End If Dim reducedFrom As MethodSymbol = method.CallsiteReducedFromMethod If reducedFrom Is Nothing Then Return End If ' This is an extension method call If arguments.IsEmpty Then arguments = ImmutableArray.Create(Of BoundExpression)(receiver) Else Dim array(arguments.Length) As BoundExpression array(0) = receiver arguments.CopyTo(array, 1) arguments = array.AsImmutableOrNull() End If receiver = Nothing method = reducedFrom End Sub Public Overrides Function VisitByRefArgumentWithCopyBack(node As BoundByRefArgumentWithCopyBack) As BoundNode Throw ExceptionUtilities.Unreachable End Function Private Function RewriteCallArguments( arguments As ImmutableArray(Of BoundExpression), parameters As ImmutableArray(Of ParameterSymbol), <Out()> ByRef temporaries As ImmutableArray(Of SynthesizedLocal), <Out()> ByRef copyBack As ImmutableArray(Of BoundExpression), suppressObjectClone As Boolean ) As ImmutableArray(Of BoundExpression) temporaries = Nothing copyBack = Nothing If arguments.IsEmpty Then Return arguments End If Dim tempsArray As ArrayBuilder(Of SynthesizedLocal) = Nothing Dim copyBackArray As ArrayBuilder(Of BoundExpression) = Nothing Dim rewrittenArgs = ArrayBuilder(Of BoundExpression).GetInstance Dim changed As Boolean = False Dim paramIdx = 0 For Each argument In arguments Dim rewritten As BoundExpression If argument.Kind = BoundKind.ByRefArgumentWithCopyBack Then rewritten = RewriteByRefArgumentWithCopyBack(DirectCast(argument, BoundByRefArgumentWithCopyBack), tempsArray, copyBackArray) Else rewritten = VisitExpressionNode(argument) If parameters(paramIdx).IsByRef AndAlso Not argument.IsLValue AndAlso Not _inExpressionLambda Then rewritten = PassArgAsTempClone(argument, rewritten, tempsArray) End If End If If Not suppressObjectClone AndAlso (Not parameters(paramIdx).IsByRef OrElse Not rewritten.IsLValue) Then rewritten = GenerateObjectCloneIfNeeded(argument, rewritten) End If If rewritten IsNot argument Then changed = True End If rewrittenArgs.Add(rewritten) paramIdx += 1 Next Debug.Assert(temporaries.IsDefault OrElse changed) If changed Then arguments = rewrittenArgs.ToImmutable() End If rewrittenArgs.Free() If tempsArray IsNot Nothing Then temporaries = tempsArray.ToImmutableAndFree() End If If copyBackArray IsNot Nothing Then ' It might feel strange, but Dev11 evaluates copy-back assignments in reverse order (from last argument to the first), ' which is observable. Doing the same thing. copyBackArray.ReverseContents() copyBack = copyBackArray.ToImmutableAndFree() End If Return arguments End Function Private Function PassArgAsTempClone( argument As BoundExpression, rewrittenArgument As BoundExpression, ByRef tempsArray As ArrayBuilder(Of SynthesizedLocal) ) As BoundExpression ' Need to allocate a temp of the target type, ' init it with argument's value, ' pass it ByRef If tempsArray Is Nothing Then tempsArray = ArrayBuilder(Of SynthesizedLocal).GetInstance() End If Dim temp = New SynthesizedLocal(Me._currentMethodOrLambda, rewrittenArgument.Type, SynthesizedLocalKind.LoweringTemp) tempsArray.Add(temp) Dim boundTemp = New BoundLocal(rewrittenArgument.Syntax, temp, temp.Type) Dim storeVal As BoundExpression = New BoundAssignmentOperator(rewrittenArgument.Syntax, boundTemp, GenerateObjectCloneIfNeeded(argument, rewrittenArgument), True, rewrittenArgument.Type) Return New BoundSequence(rewrittenArgument.Syntax, ImmutableArray(Of LocalSymbol).Empty, ImmutableArray.Create(storeVal), boundTemp, rewrittenArgument.Type) End Function Private Function RewriteByRefArgumentWithCopyBack( argument As BoundByRefArgumentWithCopyBack, ByRef tempsArray As ArrayBuilder(Of SynthesizedLocal), ByRef copyBackArray As ArrayBuilder(Of BoundExpression) ) As BoundExpression ' Need to allocate a temp of the target type, ' init it with argument's value, ' pass it ByRef, ' copy value back after the call. Dim originalArgument As BoundExpression = argument.OriginalArgument If originalArgument.IsPropertyOrXmlPropertyAccess Then Debug.Assert(originalArgument.GetAccessKind() = If( originalArgument.IsPropertyReturnsByRef(), PropertyAccessKind.Get, PropertyAccessKind.Get Or PropertyAccessKind.Set)) originalArgument = originalArgument.SetAccessKind(PropertyAccessKind.Unknown) ElseIf originalArgument.IsLateBound() Then Debug.Assert(originalArgument.GetLateBoundAccessKind() = (LateBoundAccessKind.Get Or LateBoundAccessKind.Set)) originalArgument = originalArgument.SetLateBoundAccessKind(LateBoundAccessKind.Unknown) End If If _inExpressionLambda Then If originalArgument.IsPropertyOrXmlPropertyAccess Then Debug.Assert(originalArgument.GetAccessKind() = PropertyAccessKind.Unknown) originalArgument = originalArgument.SetAccessKind(PropertyAccessKind.Get) ElseIf originalArgument.IsLateBound() Then Debug.Assert(originalArgument.GetLateBoundAccessKind() = LateBoundAccessKind.Unknown) originalArgument = originalArgument.SetLateBoundAccessKind(LateBoundAccessKind.Get) End If If originalArgument.IsLValue Then originalArgument = originalArgument.MakeRValue End If AddPlaceholderReplacement(argument.InPlaceholder, VisitExpressionNode(originalArgument)) Dim rewrittenArgumentInConversion As BoundExpression = VisitExpression(argument.InConversion) RemovePlaceholderReplacement(argument.InPlaceholder) Return rewrittenArgumentInConversion End If If tempsArray Is Nothing Then tempsArray = ArrayBuilder(Of SynthesizedLocal).GetInstance() End If If copyBackArray Is Nothing Then copyBackArray = ArrayBuilder(Of BoundExpression).GetInstance() End If Dim firstUse As BoundExpression Dim secondUse As BoundExpression Dim useTwice As UseTwiceRewriter.Result = UseTwiceRewriter.UseTwice(Me._currentMethodOrLambda, originalArgument, isForRegularCompoundAssignment:=False, tempsArray) If originalArgument.IsPropertyOrXmlPropertyAccess Then firstUse = useTwice.First.SetAccessKind(PropertyAccessKind.Get).MakeRValue() secondUse = useTwice.Second.SetAccessKind(If(originalArgument.IsPropertyReturnsByRef(), PropertyAccessKind.Get, PropertyAccessKind.Set)) ElseIf originalArgument.IsLateBound() Then firstUse = useTwice.First.SetLateBoundAccessKind(LateBoundAccessKind.Get) secondUse = useTwice.Second.SetLateBoundAccessKind(LateBoundAccessKind.Set) Else firstUse = useTwice.First.MakeRValue() secondUse = useTwice.Second Debug.Assert(secondUse.IsLValue) End If AddPlaceholderReplacement(argument.InPlaceholder, VisitExpressionNode(firstUse)) Dim inputValue As BoundExpression = VisitAndGenerateObjectCloneIfNeeded(argument.InConversion) RemovePlaceholderReplacement(argument.InPlaceholder) Dim temp = New SynthesizedLocal(Me._currentMethodOrLambda, argument.Type, SynthesizedLocalKind.LoweringTemp) tempsArray.Add(temp) Dim boundTemp = New BoundLocal(argument.Syntax, temp, temp.Type) Dim storeVal As BoundExpression = New BoundAssignmentOperator(argument.Syntax, boundTemp, inputValue, True, argument.Type) AddPlaceholderReplacement(argument.OutPlaceholder, boundTemp.MakeRValue()) Dim copyBack As BoundExpression If Not originalArgument.IsLateBound() Then copyBack = DirectCast( VisitAssignmentOperator(New BoundAssignmentOperator(argument.Syntax, secondUse, argument.OutConversion, False)), BoundExpression) RemovePlaceholderReplacement(argument.OutPlaceholder) Else Dim copyBackValue As BoundExpression = VisitExpressionNode(argument.OutConversion) RemovePlaceholderReplacement(argument.OutPlaceholder) If secondUse.Kind = BoundKind.LateMemberAccess Then ' Method(ref objExpr.goo) copyBack = LateSet(secondUse.Syntax, DirectCast(MyBase.VisitLateMemberAccess(DirectCast(secondUse, BoundLateMemberAccess)), BoundLateMemberAccess), copyBackValue, Nothing, Nothing, isCopyBack:=True) Else Dim invocation = DirectCast(secondUse, BoundLateInvocation) If invocation.Member.Kind = BoundKind.LateMemberAccess Then ' Method(ref objExpr.goo(args)) copyBack = LateSet(invocation.Syntax, DirectCast(MyBase.VisitLateMemberAccess(DirectCast(invocation.Member, BoundLateMemberAccess)), BoundLateMemberAccess), copyBackValue, VisitList(invocation.ArgumentsOpt), invocation.ArgumentNamesOpt, isCopyBack:=True) Else ' Method(ref objExpr(args)) invocation = invocation.Update(VisitExpressionNode(invocation.Member), VisitList(invocation.ArgumentsOpt), invocation.ArgumentNamesOpt, invocation.AccessKind, invocation.MethodOrPropertyGroupOpt, invocation.Type) copyBack = LateIndexSet(invocation.Syntax, invocation, copyBackValue, isCopyBack:=True) End If End If End If copyBackArray.Add(copyBack) Return New BoundSequence(argument.Syntax, ImmutableArray(Of LocalSymbol).Empty, ImmutableArray.Create(storeVal), boundTemp, argument.Type) End Function End Class End Namespace