|
' 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.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Namespace Microsoft.CodeAnalysis.VisualBasic
Partial Friend Class LocalRewriter
Public Overrides Function VisitXmlComment(node As BoundXmlComment) As BoundNode
Return Visit(node.ObjectCreation)
End Function
Public Overrides Function VisitXmlDocument(node As BoundXmlDocument) As BoundNode
If Me._inExpressionLambda AndAlso Not node.HasErrors Then
Return VisitXmlContainerInExpressionLambda(node.RewriterInfo)
Else
Return VisitXmlContainer(node.RewriterInfo)
End If
End Function
Public Overrides Function VisitXmlDeclaration(node As BoundXmlDeclaration) As BoundNode
Return Visit(node.ObjectCreation)
End Function
Public Overrides Function VisitXmlProcessingInstruction(node As BoundXmlProcessingInstruction) As BoundNode
Return Visit(node.ObjectCreation)
End Function
Public Overrides Function VisitXmlAttribute(node As BoundXmlAttribute) As BoundNode
Return Visit(node.ObjectCreation)
End Function
Public Overrides Function VisitXmlElement(node As BoundXmlElement) As BoundNode
Dim rewriterInfo = node.RewriterInfo
Dim previousImportedNamespaces = Me._xmlImportedNamespaces
If rewriterInfo.IsRoot Then
Debug.Assert(Not rewriterInfo.ImportedNamespaces.IsDefault)
Me._xmlImportedNamespaces = rewriterInfo.ImportedNamespaces
End If
Dim result As BoundNode
If Me._inExpressionLambda AndAlso Not node.HasErrors Then
result = VisitXmlContainerInExpressionLambda(rewriterInfo)
Else
result = VisitXmlContainer(rewriterInfo)
End If
If rewriterInfo.IsRoot Then
Me._xmlImportedNamespaces = previousImportedNamespaces
End If
Return result
End Function
Public Overrides Function VisitXmlEmbeddedExpression(node As BoundXmlEmbeddedExpression) As BoundNode
Return Visit(node.Expression)
End Function
Public Overrides Function VisitXmlMemberAccess(node As BoundXmlMemberAccess) As BoundNode
Return Visit(node.MemberAccess)
End Function
Public Overrides Function VisitXmlName(node As BoundXmlName) As BoundNode
Return Visit(node.ObjectCreation)
End Function
Public Overrides Function VisitXmlNamespace(node As BoundXmlNamespace) As BoundNode
Return Visit(node.ObjectCreation)
End Function
Public Overrides Function VisitXmlCData(node As BoundXmlCData) As BoundNode
Return Visit(node.ObjectCreation)
End Function
Private Function VisitXmlContainer(rewriterInfo As BoundXmlContainerRewriterInfo) As BoundExpression
Debug.Assert(Not Me._inExpressionLambda)
Dim expr = VisitExpressionNode(rewriterInfo.ObjectCreation)
If rewriterInfo.SideEffects.Length = 0 Then
Debug.Assert(rewriterInfo.XmlnsAttributesPlaceholder Is Nothing)
Return expr
End If
Dim syntax = expr.Syntax
Dim type = expr.Type
Dim locals = ArrayBuilder(Of LocalSymbol).GetInstance()
Dim sideEffects = ArrayBuilder(Of BoundExpression).GetInstance()
Dim local = CreateTempLocal(syntax, type, expr, sideEffects)
locals.Add(local.LocalSymbol)
AddPlaceholderReplacement(rewriterInfo.Placeholder, local)
Dim attributes As BoundLocal = Nothing
If rewriterInfo.XmlnsAttributesPlaceholder IsNot Nothing Then
attributes = CreateTempLocal(syntax, rewriterInfo.XmlnsAttributesPlaceholder.Type, VisitExpressionNode(rewriterInfo.XmlnsAttributes), sideEffects)
locals.Add(attributes.LocalSymbol)
AddPlaceholderReplacement(rewriterInfo.XmlnsAttributesPlaceholder, attributes)
End If
If rewriterInfo.PrefixesPlaceholder IsNot Nothing Then
Dim prefixesArray As BoundExpression = Nothing
Dim namespacesArray As BoundExpression = Nothing
CreatePrefixesAndNamespacesArrays(rewriterInfo, syntax, prefixesArray, namespacesArray)
Dim prefixes = CreateTempLocal(syntax, rewriterInfo.PrefixesPlaceholder.Type, prefixesArray, sideEffects)
locals.Add(prefixes.LocalSymbol)
AddPlaceholderReplacement(rewriterInfo.PrefixesPlaceholder, prefixes)
Dim namespaces = CreateTempLocal(syntax, rewriterInfo.NamespacesPlaceholder.Type, namespacesArray, sideEffects)
locals.Add(namespaces.LocalSymbol)
AddPlaceholderReplacement(rewriterInfo.NamespacesPlaceholder, namespaces)
End If
VisitList(rewriterInfo.SideEffects, sideEffects)
If rewriterInfo.PrefixesPlaceholder IsNot Nothing Then
RemovePlaceholderReplacement(rewriterInfo.PrefixesPlaceholder)
RemovePlaceholderReplacement(rewriterInfo.NamespacesPlaceholder)
End If
If rewriterInfo.XmlnsAttributesPlaceholder IsNot Nothing Then
RemovePlaceholderReplacement(rewriterInfo.XmlnsAttributesPlaceholder)
End If
RemovePlaceholderReplacement(rewriterInfo.Placeholder)
Return New BoundSequence(syntax,
locals:=locals.ToImmutableAndFree(),
sideEffects:=sideEffects.ToImmutableAndFree(),
valueOpt:=local,
type:=type)
End Function
''' <summary>
''' Create a temp local of the given type and initial value.
''' The resulting local is treated as an rvalue, and the
''' initialization assignment is added to 'sideEffects'.
''' </summary>
Private Function CreateTempLocal(syntax As SyntaxNode, type As TypeSymbol, expr As BoundExpression, sideEffects As ArrayBuilder(Of BoundExpression)) As BoundLocal
Dim local = New BoundLocal(syntax, New SynthesizedLocal(Me._currentMethodOrLambda, type, SynthesizedLocalKind.LoweringTemp), type)
sideEffects.Add(New BoundAssignmentOperator(syntax, local, expr, suppressObjectClone:=True, type:=type))
Return local.MakeRValue()
End Function
Private Function VisitXmlContainerInExpressionLambda(rewriterInfo As BoundXmlContainerRewriterInfo) As BoundExpression
Debug.Assert(Me._inExpressionLambda)
' NOTE: When we convert Xml container expression in context of expression lambda we rewrite it into
' one of the following: New XElement(XName, Object), New XElement(XName, Object()) or
' New XDocument(XName, Object()) where the second parameter, an object or object array, contains all
' expressions from rewriterInfo.SideEffects (which are supposed to be in a form of <l>.Add(<e>));
'
' In case there are any extra locals introduced by rewriterInfo.XmlnsAttributes, rewriterInfo.Prefixes
' or rewriterInfo.Namespaces, those need to be put into preamble for the method so that they are
' initialized outside expression tree lambda and captured properly
Dim origSideEffects As ImmutableArray(Of BoundExpression) = rewriterInfo.SideEffects
Debug.Assert(rewriterInfo.ObjectCreation.Kind = BoundKind.ObjectCreationExpression)
Dim objCreation = DirectCast(rewriterInfo.ObjectCreation, BoundObjectCreationExpression)
' if there are no side effects just rewrite object creation expression
If origSideEffects.Length = 0 Then
Debug.Assert(rewriterInfo.XmlnsAttributesPlaceholder Is Nothing)
Return VisitExpressionNode(objCreation)
End If
Dim origArgument As BoundExpression = objCreation.Arguments(0)
Debug.Assert(objCreation.InitializerOpt Is Nothing)
Debug.Assert(objCreation.ConstructorOpt IsNot Nothing)
' NOTE: if we are here, original bound node does not have errors, thus we
' can assume the symbols used in this node are OK
Dim constructor As MethodSymbol = Nothing
If objCreation.Arguments.Length = 1 Then
' This is a branch where XElement::.ctor(XName) lands, we need to get XElement::.ctor(XName, Object())
Debug.Assert(TypeSymbol.Equals(objCreation.ConstructorOpt.ContainingType, Me.Compilation.GetWellKnownType(WellKnownType.System_Xml_Linq_XElement), TypeCompareKind.ConsiderEverything))
constructor = DirectCast(Me.Compilation.GetWellKnownTypeMember(If(origSideEffects.Length = 1,
WellKnownMember.System_Xml_Linq_XElement__ctor,
WellKnownMember.System_Xml_Linq_XElement__ctor2)), MethodSymbol)
If ReportMissingOrBadRuntimeHelper(objCreation, WellKnownMember.System_Xml_Linq_XElement__ctor2, constructor) Then
Return VisitExpressionNode(objCreation)
End If
Else
' This is a branch where XDocument::.ctor(XDeclaration, Object()) lands
Debug.Assert(objCreation.Arguments.Length = 2)
constructor = objCreation.ConstructorOpt
Debug.Assert(TypeSymbol.Equals(constructor.ContainingType, Me.Compilation.GetWellKnownType(WellKnownType.System_Xml_Linq_XDocument), TypeCompareKind.ConsiderEverything))
End If
Dim syntax = objCreation.Syntax
Dim type = objCreation.Type
' NOTE: rewriterInfo.Placeholder should never be used in rewriting below, because it is supposed
' to be on the receiver side of side-effect calls and we never rewrite those
'AddPlaceholderReplacement(rewriterInfo.Placeholder, local)
Dim attributes As BoundLocal = Nothing
If rewriterInfo.XmlnsAttributesPlaceholder IsNot Nothing Then
attributes = CreateTempLocalInExpressionLambda(syntax, rewriterInfo.XmlnsAttributesPlaceholder.Type, VisitExpressionNode(rewriterInfo.XmlnsAttributes))
AddPlaceholderReplacement(rewriterInfo.XmlnsAttributesPlaceholder, attributes)
End If
If rewriterInfo.PrefixesPlaceholder IsNot Nothing Then
Dim prefixesArray As BoundExpression = Nothing
Dim namespacesArray As BoundExpression = Nothing
CreatePrefixesAndNamespacesArrays(rewriterInfo, syntax, prefixesArray, namespacesArray)
Dim prefixes = CreateTempLocalInExpressionLambda(syntax, rewriterInfo.PrefixesPlaceholder.Type, prefixesArray)
AddPlaceholderReplacement(rewriterInfo.PrefixesPlaceholder, prefixes)
Dim namespaces = CreateTempLocalInExpressionLambda(syntax, rewriterInfo.NamespacesPlaceholder.Type, namespacesArray)
AddPlaceholderReplacement(rewriterInfo.NamespacesPlaceholder, namespaces)
End If
Dim rewrittenCallArguments(origSideEffects.Length - 1) As BoundExpression
For i = 0 To origSideEffects.Length - 1
Debug.Assert(origSideEffects(i).Kind = BoundKind.Call)
Dim [call] = DirectCast(origSideEffects(i), BoundCall)
Debug.Assert([call].Arguments.Length = 1)
rewrittenCallArguments(i) = VisitExpressionNode([call].Arguments(0))
Next
If rewriterInfo.PrefixesPlaceholder IsNot Nothing Then
RemovePlaceholderReplacement(rewriterInfo.PrefixesPlaceholder)
RemovePlaceholderReplacement(rewriterInfo.NamespacesPlaceholder)
End If
If rewriterInfo.XmlnsAttributesPlaceholder IsNot Nothing Then
RemovePlaceholderReplacement(rewriterInfo.XmlnsAttributesPlaceholder)
End If
' See comment above
'RemovePlaceholderReplacement(rewriterInfo.Placeholder)
Dim secondArgumentType As TypeSymbol = constructor.Parameters(1).Type
Dim secondArgument As BoundExpression
If secondArgumentType.IsArrayType Then
' Second parameter is an object array
Dim arrayType = DirectCast(secondArgumentType, ArrayTypeSymbol)
secondArgument = New BoundArrayCreation(
objCreation.Syntax,
ImmutableArray.Create(Of BoundExpression)(
New BoundLiteral(
objCreation.Syntax,
ConstantValue.Create(rewrittenCallArguments.Length),
Me.GetSpecialType(SpecialType.System_Int32))),
New BoundArrayInitialization(
objCreation.Syntax, rewrittenCallArguments.AsImmutableOrNull, arrayType),
arrayType)
Else
' Second parameter is an object; there must be only one argument
Debug.Assert(rewrittenCallArguments.Length = 1)
secondArgument = rewrittenCallArguments(0)
End If
Return objCreation.Update(constructor,
ImmutableArray.Create(Of BoundExpression)(
VisitExpression(origArgument), secondArgument),
defaultArguments:=Nothing,
Nothing,
objCreation.Type)
End Function
Private Function CreateTempLocalInExpressionLambda(syntax As SyntaxNode, type As TypeSymbol, expr As BoundExpression) As BoundLocal
Dim local As New SynthesizedLocal(Me._topMethod, type, SynthesizedLocalKind.XmlInExpressionLambda, Me._currentMethodOrLambda.Syntax)
Dim boundLocal = New BoundLocal(syntax, local, type)
Me._xmlFixupData.AddLocal(local, New BoundAssignmentOperator(syntax, boundLocal, expr, suppressObjectClone:=True, type:=type))
Return boundLocal.MakeRValue()
End Function
Private Sub CreatePrefixesAndNamespacesArrays(
rewriterInfo As BoundXmlContainerRewriterInfo,
syntax As SyntaxNode,
<Out()> ByRef prefixes As BoundExpression,
<Out()> ByRef namespaces As BoundExpression)
Dim prefixesBuilder = ArrayBuilder(Of BoundExpression).GetInstance()
Dim namespacesBuilder = ArrayBuilder(Of BoundExpression).GetInstance()
For Each pair In Me._xmlImportedNamespaces
prefixesBuilder.Add(CreateCompilerGeneratedXmlnsPrefix(syntax, pair.Key))
namespacesBuilder.Add(CreateCompilerGeneratedXmlNamespace(syntax, pair.Value))
Next
For Each pair In rewriterInfo.InScopeXmlNamespaces
prefixesBuilder.Add(CreateCompilerGeneratedXmlnsPrefix(syntax, pair.Key))
namespacesBuilder.Add(CreateCompilerGeneratedXmlNamespace(syntax, pair.Value))
Next
prefixes = VisitExpressionNode(CreateCompilerGeneratedArray(syntax, rewriterInfo.PrefixesPlaceholder.Type, prefixesBuilder.ToImmutableAndFree()))
namespaces = VisitExpressionNode(CreateCompilerGeneratedArray(syntax, rewriterInfo.NamespacesPlaceholder.Type, namespacesBuilder.ToImmutableAndFree()))
End Sub
Private Function BindXmlNamespace(syntax As SyntaxNode, [namespace] As BoundExpression) As BoundExpression
' XNamespace.Get must exist since we only add XNamespaces in
' lowering if corresponding XNamespaces were created when binding
' the containing XElement. (See Binder.BindXmlContainerRewriterInfo
' where we add xmlns attributes for any prefixes required from Imports.)
Dim method = DirectCast(Compilation.GetWellKnownTypeMember(WellKnownMember.System_Xml_Linq_XNamespace__Get), MethodSymbol)
Return New BoundCall(syntax,
method,
methodGroupOpt:=Nothing,
receiverOpt:=Nothing,
arguments:=ImmutableArray.Create([namespace]),
constantValueOpt:=Nothing,
type:=method.ReturnType).MakeCompilerGenerated()
End Function
Private Function CreateStringLiteral(syntax As SyntaxNode, str As String) As BoundLiteral
Return New BoundLiteral(syntax, ConstantValue.Create(str), GetSpecialType(SpecialType.System_String)).MakeCompilerGenerated()
End Function
Private Function CreateCompilerGeneratedXmlnsPrefix(syntax As SyntaxNode, prefix As String) As BoundExpression
Return CreateStringLiteral(syntax, If(prefix = StringConstants.DefaultXmlnsPrefix, StringConstants.XmlnsPrefix, prefix))
End Function
Private Function CreateCompilerGeneratedXmlNamespace(syntax As SyntaxNode, [namespace] As String) As BoundExpression
Return BindXmlNamespace(syntax, CreateStringLiteral(syntax, [namespace]))
End Function
''' <summary>
''' Create a BoundExpression representing an array creation initialized with the given items.
''' If there are zero items, the result is a BoundLiteral Nothing, otherwise, a BoundArrayCreation.
''' </summary>
Private Function CreateCompilerGeneratedArray(
syntax As SyntaxNode,
arrayType As TypeSymbol,
items As ImmutableArray(Of BoundExpression)) As BoundExpression
Dim result As BoundExpression
If items.Length = 0 Then
result = New BoundLiteral(syntax, ConstantValue.Nothing, arrayType)
Else
Dim size = (New BoundLiteral(syntax, ConstantValue.Create(items.Length), GetSpecialType(SpecialType.System_Int32))).MakeCompilerGenerated()
Dim initializer = (New BoundArrayInitialization(syntax, items, arrayType)).MakeCompilerGenerated()
result = New BoundArrayCreation(syntax, ImmutableArray.Create(Of BoundExpression)(size), initializer, arrayType)
End If
result.SetWasCompilerGenerated()
Return result
End Function
End Class
End Namespace
|