/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectCreation.vb
430 строк
24 KB
AlekseyTs
Rebind an initializer shared across multiple symbols. (#54304)
24 июн 2021, 07:58
Не верифицирован
24 июн 2021, 07:58
5db3a5e
Код
Авторство
О чём код?
' 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 Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic Partial Friend NotInheritable Class LocalRewriter Public Overrides Function VisitObjectCreationExpression(node As BoundObjectCreationExpression) As BoundNode ' save the object initializer away to rewrite them later on and set the initializers to nothing to not rewrite them ' two times. Dim objectInitializer = node.InitializerOpt node = node.Update(node.ConstructorOpt, node.Arguments, node.DefaultArguments, Nothing, node.Type) Dim ctor = node.ConstructorOpt Dim result As BoundExpression = node If ctor IsNot Nothing Then Dim temporaries As ImmutableArray(Of SynthesizedLocal) = Nothing Dim copyBack As ImmutableArray(Of BoundExpression) = Nothing result = node.Update(ctor, RewriteCallArguments(node.Arguments, ctor.Parameters, temporaries, copyBack, False), node.DefaultArguments, Nothing, ctor.ContainingType) If Not temporaries.IsDefault Then result = GenerateSequenceValueSideEffects(_currentMethodOrLambda, result, StaticCast(Of LocalSymbol).From(temporaries), copyBack) End If ' If a coclass was instantiated, convert the class to the interface type. If node.Type.IsInterfaceType() Then Debug.Assert(result.Type.Equals(DirectCast(node.Type, NamedTypeSymbol).CoClassType)) Dim useSiteInfo = GetNewCompoundUseSiteInfo() Dim conv As ConversionKind = Conversions.ClassifyDirectCastConversion(result.Type, node.Type, useSiteInfo) Debug.Assert(Conversions.ConversionExists(conv)) _diagnostics.Add(result, useSiteInfo) result = New BoundDirectCast(node.Syntax, result, conv, node.Type, Nothing) Else Debug.Assert(node.Type.IsSameTypeIgnoringAll(result.Type)) End If End If If objectInitializer IsNot Nothing Then Return VisitObjectCreationInitializer(objectInitializer, node, result) End If Return result End Function Public Overrides Function VisitNoPiaObjectCreationExpression(node As BoundNoPiaObjectCreationExpression) As BoundNode ' For the NoPIA feature, we need to gather the GUID from the coclass, and ' generate the following: ' DirectCast(System.Activator.CreateInstance(System.Runtime.InteropServices.Marshal.GetTypeFromCLSID(New Guid(GUID))), IPiaType) ' ' If System.Runtime.InteropServices.Marshal.GetTypeFromCLSID is not available (older framework), ' System.Type.GetTypeFromCLSID() is used to get the type for the CLSID. Dim factory As New SyntheticBoundNodeFactory(_topMethod, _currentMethodOrLambda, node.Syntax, _compilationState, _diagnostics) Dim ctor = factory.WellKnownMember(Of MethodSymbol)(WellKnownMember.System_Guid__ctor) Dim newGuid As BoundExpression If ctor IsNot Nothing Then newGuid = factory.[New](ctor, factory.Literal(node.GuidString)) Else newGuid = New BoundBadExpression(node.Syntax, LookupResultKind.NotCreatable, ImmutableArray(Of Symbol).Empty, ImmutableArray(Of BoundExpression).Empty, ErrorTypeSymbol.UnknownResultType, hasErrors:=True) End If Dim getTypeFromCLSID = If(factory.WellKnownMember(Of MethodSymbol)(WellKnownMember.System_Runtime_InteropServices_Marshal__GetTypeFromCLSID, isOptional:=True), factory.WellKnownMember(Of MethodSymbol)(WellKnownMember.System_Type__GetTypeFromCLSID)) Dim callGetTypeFromCLSID As BoundExpression If getTypeFromCLSID IsNot Nothing Then callGetTypeFromCLSID = factory.Call(Nothing, getTypeFromCLSID, newGuid) Else callGetTypeFromCLSID = New BoundBadExpression(node.Syntax, LookupResultKind.OverloadResolutionFailure, ImmutableArray(Of Symbol).Empty, ImmutableArray(Of BoundExpression).Empty, ErrorTypeSymbol.UnknownResultType, hasErrors:=True) End If Dim createInstance = factory.WellKnownMember(Of MethodSymbol)(WellKnownMember.System_Activator__CreateInstance) Dim rewrittenObjectCreation As BoundExpression If createInstance IsNot Nothing AndAlso Not createInstance.ReturnType.IsErrorType() Then Dim useSiteInfo = GetNewCompoundUseSiteInfo() Dim conversion = Conversions.ClassifyDirectCastConversion(createInstance.ReturnType, node.Type, useSiteInfo) _diagnostics.Add(node, useSiteInfo) rewrittenObjectCreation = New BoundDirectCast(node.Syntax, factory.Call(Nothing, createInstance, callGetTypeFromCLSID), conversion, node.Type) Else rewrittenObjectCreation = New BoundBadExpression(node.Syntax, LookupResultKind.OverloadResolutionFailure, ImmutableArray(Of Symbol).Empty, ImmutableArray(Of BoundExpression).Empty, node.Type, hasErrors:=True) End If If node.InitializerOpt Is Nothing OrElse node.InitializerOpt.HasErrors Then Return rewrittenObjectCreation End If Return VisitObjectCreationInitializer(node.InitializerOpt, node, rewrittenObjectCreation) End Function Private Function VisitObjectCreationInitializer( objectInitializer As BoundObjectInitializerExpressionBase, objectCreationExpression As BoundExpression, rewrittenObjectCreationExpression As BoundExpression ) As BoundNode If objectInitializer.Kind = BoundKind.CollectionInitializerExpression Then Return RewriteCollectionInitializerExpression(DirectCast(objectInitializer, BoundCollectionInitializerExpression), objectCreationExpression, rewrittenObjectCreationExpression) Else Return RewriteObjectInitializerExpression(DirectCast(objectInitializer, BoundObjectInitializerExpression), objectCreationExpression, rewrittenObjectCreationExpression) End If End Function Public Overrides Function VisitNewT(node As BoundNewT) As BoundNode ' Unlike C#, "New T()" is always rewritten as "Activator.CreateInstance<T>()", ' even if T is known to be a value type or reference type. This matches Dev10 VB. If _inExpressionLambda Then ' NOTE: If we are in expression lambda, we want to keep BoundNewT ' NOTE: node, but we need to rewrite initializers if any. If node.InitializerOpt IsNot Nothing Then Return VisitObjectCreationInitializer(node.InitializerOpt, node, node) Else Return node End If End If Dim syntax = node.Syntax Dim typeParameter = DirectCast(node.Type, TypeParameterSymbol) Dim result As BoundExpression Dim method As MethodSymbol = Nothing If TryGetWellknownMember(method, WellKnownMember.System_Activator__CreateInstance_T, syntax) Then Debug.Assert(method IsNot Nothing) method = method.Construct(ImmutableArray.Create(Of TypeSymbol)(typeParameter)) result = New BoundCall(syntax, method, methodGroupOpt:=Nothing, receiverOpt:=Nothing, arguments:=ImmutableArray(Of BoundExpression).Empty, constantValueOpt:=Nothing, isLValue:=False, suppressObjectClone:=False, type:=typeParameter) Else result = New BoundBadExpression(syntax, LookupResultKind.NotReferencable, ImmutableArray(Of Symbol).Empty, ImmutableArray(Of BoundExpression).Empty, typeParameter, hasErrors:=True) End If If node.InitializerOpt IsNot Nothing Then Return VisitObjectCreationInitializer(node.InitializerOpt, result, result) End If Return result End Function ''' <summary> ''' Rewrites a CollectionInitializerExpression to a list of Add calls and returns the temporary. ''' E.g. the following code: ''' Dim x As New CollectionType(param1) From {1, {2, 3}, {4, {5, 6, 7}}} ''' gets rewritten to ''' Dim temp as CollectionType ''' temp = new CollectionType(param1) ''' temp.Add(1) ''' temp.Add(2, 3) ''' temp.Add(4, {5, 6, 7}) ''' x = temp ''' where the last assignment is not part of this rewriting, because the BoundCollectionInitializerExpression ''' only represents the object creation expression with the initialization. ''' </summary> ''' <param name="node">The BoundCollectionInitializerExpression that should be rewritten.</param> ''' <returns>A bound sequence for the object creation expression containing the invocation expressions.</returns> Public Function RewriteCollectionInitializerExpression( node As BoundCollectionInitializerExpression, objectCreationExpression As BoundExpression, rewrittenObjectCreationExpression As BoundExpression ) As BoundNode Debug.Assert(node.PlaceholderOpt IsNot Nothing) Dim expressionType = node.Type Dim syntaxNode = node.Syntax Dim tempLocalSymbol As LocalSymbol Dim tempLocal As BoundLocal Dim expressions = ArrayBuilder(Of BoundExpression).GetInstance() Dim newPlaceholder As BoundWithLValueExpressionPlaceholder If _inExpressionLambda Then ' A temp is not needed for this case tempLocalSymbol = Nothing tempLocal = Nothing ' Simply replace placeholder with a copy, it will be dropped by Expression Tree rewriter. The copy is needed to ' keep the double rewrite tracking happy. newPlaceholder = New BoundWithLValueExpressionPlaceholder(node.PlaceholderOpt.Syntax, node.PlaceholderOpt.Type) AddPlaceholderReplacement(node.PlaceholderOpt, newPlaceholder) Else ' Create a temp symbol ' Dim temp as CollectionType ' Create assignment for the rewritten object ' creation expression to the temp ' temp = new CollectionType(param1) tempLocalSymbol = New SynthesizedLocal(Me._currentMethodOrLambda, expressionType, SynthesizedLocalKind.LoweringTemp) tempLocal = New BoundLocal(syntaxNode, tempLocalSymbol, expressionType) Dim temporaryAssignment = New BoundAssignmentOperator(syntaxNode, tempLocal, GenerateObjectCloneIfNeeded(objectCreationExpression, rewrittenObjectCreationExpression), suppressObjectClone:=True, type:=expressionType) expressions.Add(temporaryAssignment) newPlaceholder = Nothing AddPlaceholderReplacement(node.PlaceholderOpt, tempLocal) End If Dim initializerCount = node.Initializers.Length ' rewrite the invocation expressions and add them to the expression of the sequence ' temp.Add(...) For initializerIndex = 0 To initializerCount - 1 ' NOTE: if the method Add(...) is omitted we build a local which ' seems to be redundant, this will optimized out later ' by stack scheduler Dim initializer As BoundExpression = node.Initializers(initializerIndex) If Not IsOmittedBoundCall(initializer) Then expressions.Add(VisitExpressionNode(initializer)) End If Next RemovePlaceholderReplacement(node.PlaceholderOpt) If _inExpressionLambda Then Debug.Assert(tempLocalSymbol Is Nothing) Debug.Assert(tempLocal Is Nothing) ' NOTE: if inside expression lambda we rewrite the collection initializer ' NOTE: node and attach it back to object creation expression, it will be ' NOTE: rewritten later in ExpressionLambdaRewriter ' Rewrite object creation Return ReplaceObjectOrCollectionInitializer( rewrittenObjectCreationExpression, node.Update(newPlaceholder, expressions.ToImmutableAndFree(), node.Type)) Else Debug.Assert(tempLocalSymbol IsNot Nothing) Debug.Assert(tempLocal IsNot Nothing) Return New BoundSequence(syntaxNode, ImmutableArray.Create(Of LocalSymbol)(tempLocalSymbol), expressions.ToImmutableAndFree(), tempLocal.MakeRValue(), expressionType) End If End Function ''' <summary> ''' Rewrites a ObjectInitializerExpression to either a statement list (in case the there is no temporary used) or a bound ''' sequence expression (in case there is a temporary used). The information whether to use a temporary or not is ''' stored in the bound object member initializer node itself. ''' ''' E.g. the following code: ''' Dim x = New RefTypeName(param1) With {.FieldName1 = 23, .FieldName2 = .FieldName3, .FieldName4 = x.FieldName1} ''' gets rewritten to ''' Dim temp as RefTypeName ''' temp = new RefTypeName(param1) ''' temp.FieldName1 = 23 ''' temp.FieldName2 = temp.FieldName3 ''' temp.FieldName4 = x.FieldName1 ''' x = temp ''' where the last assignment is not part of this rewriting, because the BoundObjectInitializerExpression ''' only represents the object creation expression with the initialization. ''' ''' In a case where no temporary is used the following code: ''' Dim x As New ValueTypeName(param1) With {.FieldName1 = 23, .FieldName2 = .FieldName3, .FieldName4 = x.FieldName1} ''' gets rewritten to ''' x = new ValueTypeName(param1) ''' x.FieldName1 = 23 ''' x.FieldName2 = x.FieldName3 ''' x.FieldName4 = x.FieldName1 ''' </summary> ''' <param name="node">The BoundObjectInitializerExpression that should be rewritten.</param> ''' <returns>A bound sequence for the object creation expression containing the invocation expressions, or a ''' bound statement list if no temporary should be used.</returns> Public Function RewriteObjectInitializerExpression( node As BoundObjectInitializerExpression, objectCreationExpression As BoundExpression, rewrittenObjectCreationExpression As BoundExpression ) As BoundNode Dim targetObjectReference As BoundExpression Dim expressionType = node.Type Dim initializerCount = node.Initializers.Length Dim syntaxNode = node.Syntax Dim sequenceType As TypeSymbol Dim sequenceTemporaries As ImmutableArray(Of LocalSymbol) Dim sequenceValueExpression As BoundExpression Debug.Assert(node.PlaceholderOpt IsNot Nothing) ' NOTE: If we are in an expression lambda not all object initializers are allowed, essentially ' NOTE: everything requiring temp local creation is disabled; this rule is not applicable to ' NOTE: locals that are created and ONLY used on left-hand-side of initializer assignments, ' NOTE: ExpressionLambdaRewriter will get rid of them ' NOTE: In order ExpressionLambdaRewriter to be able to detect such locals being used on the ' NOTE: *right* side of initializer assignments we rewrite node.PlaceholderOpt into itself If node.CreateTemporaryLocalForInitialization Then ' create temporary ' Dim temp as RefTypeName Dim tempLocalSymbol As LocalSymbol = New SynthesizedLocal(Me._currentMethodOrLambda, expressionType, SynthesizedLocalKind.LoweringTemp) sequenceType = expressionType sequenceTemporaries = ImmutableArray.Create(Of LocalSymbol)(tempLocalSymbol) targetObjectReference = If(_inExpressionLambda, DirectCast(node.PlaceholderOpt, BoundExpression), New BoundLocal(syntaxNode, tempLocalSymbol, expressionType)) sequenceValueExpression = targetObjectReference.MakeRValue() AddPlaceholderReplacement(node.PlaceholderOpt, targetObjectReference) Else ' Get the receiver for the current initialized variable in case of an "AsNew" declaration ' this is the only case where there might be no temporary needed. ' The replacement for this placeholder was added in VisitAsNewLocalDeclarations. targetObjectReference = PlaceholderReplacement(node.PlaceholderOpt) sequenceType = GetSpecialType(SpecialType.System_Void) sequenceTemporaries = ImmutableArray(Of LocalSymbol).Empty sequenceValueExpression = Nothing End If Dim sequenceExpressions(initializerCount) As BoundExpression ' create assignment for object creation expression to temporary or variable declaration ' x = new TypeName(...) ' or ' temp = new TypeName(...) sequenceExpressions(0) = New BoundAssignmentOperator(syntaxNode, targetObjectReference, GenerateObjectCloneIfNeeded(objectCreationExpression, rewrittenObjectCreationExpression), suppressObjectClone:=True, type:=expressionType) ' rewrite the assignment expressions and add them to the statement list ' x.FieldName = value expression ' or ' temp.FieldName = value expression For initializerIndex = 0 To initializerCount - 1 If _inExpressionLambda Then ' NOTE: Inside expression lambda we rewrite only right-hand-side of the assignments, left part ' NOTE: will be kept unchanged to make sure we got proper symbol out of it Dim assignment = DirectCast(node.Initializers(initializerIndex), BoundAssignmentOperator) Debug.Assert(assignment.LeftOnTheRightOpt Is Nothing) sequenceExpressions(initializerIndex + 1) = assignment.Update(assignment.Left, assignment.LeftOnTheRightOpt, VisitExpressionNode(assignment.Right), True, assignment.Type) Else sequenceExpressions(initializerIndex + 1) = VisitExpressionNode(node.Initializers(initializerIndex)) End If Next If node.CreateTemporaryLocalForInitialization Then RemovePlaceholderReplacement(node.PlaceholderOpt) End If If _inExpressionLambda Then ' when converting object initializer inside expression lambdas we want to keep ' object initializer in object creation expression; we just store visited initializers ' back to the original object initializer and update the original object creation expression ' create new initializers Dim newInitializers(initializerCount - 1) As BoundExpression Dim errors As Boolean = False For index = 0 To initializerCount - 1 newInitializers(index) = sequenceExpressions(index + 1) Next ' Rewrite object creation Return ReplaceObjectOrCollectionInitializer( rewrittenObjectCreationExpression, node.Update(node.CreateTemporaryLocalForInitialization, node.PlaceholderOpt, newInitializers.AsImmutableOrNull(), node.Type)) End If Return New BoundSequence(syntaxNode, sequenceTemporaries, sequenceExpressions.AsImmutableOrNull, sequenceValueExpression, sequenceType) End Function Private Function ReplaceObjectOrCollectionInitializer(rewrittenObjectCreationExpression As BoundExpression, rewrittenInitializer As BoundObjectInitializerExpressionBase) As BoundExpression Select Case rewrittenObjectCreationExpression.Kind Case BoundKind.ObjectCreationExpression Dim objCreation = DirectCast(rewrittenObjectCreationExpression, BoundObjectCreationExpression) Return objCreation.Update(objCreation.ConstructorOpt, objCreation.Arguments, objCreation.DefaultArguments, rewrittenInitializer, objCreation.Type) Case BoundKind.NewT Dim newT = DirectCast(rewrittenObjectCreationExpression, BoundNewT) Return newT.Update(rewrittenInitializer, newT.Type) Case BoundKind.Sequence ' NOTE: is rewrittenObjectCreationExpression is not an object creation expression, it ' NOTE: was probably wrapped with sequence which means that this case is not supported ' NOTE: inside expression lambdas. Dim sequence = DirectCast(rewrittenObjectCreationExpression, BoundSequence) Debug.Assert(sequence.ValueOpt IsNot Nothing AndAlso sequence.ValueOpt.Kind = BoundKind.ObjectCreationExpression) Return sequence.Update(sequence.Locals, sequence.SideEffects, ReplaceObjectOrCollectionInitializer(sequence.ValueOpt, rewrittenInitializer), sequence.Type) Case Else Throw ExceptionUtilities.UnexpectedValue(rewrittenObjectCreationExpression.Kind) End Select End Function End Class End Namespace