|
' 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.Globalization
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.CodeGeneration
Imports Microsoft.CodeAnalysis.CodeGeneration.CodeGenerationHelpers
Imports Microsoft.CodeAnalysis.Editing
Imports Microsoft.CodeAnalysis.Simplification
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration
Partial Friend Module ExpressionGenerator
Private Const s_doubleQuote = """"
Public Function GenerateExpression(generator As SyntaxGenerator, typedConstant As TypedConstant) As ExpressionSyntax
Select Case typedConstant.Kind
Case TypedConstantKind.Primitive, TypedConstantKind.Enum
Return GenerateExpression(generator, typedConstant.Type, typedConstant.Value, canUseFieldReference:=True)
Case TypedConstantKind.Array
If typedConstant.IsNull Then
Return GenerateNothingLiteral()
Else
Return SyntaxFactory.CollectionInitializer(
SyntaxFactory.SeparatedList(typedConstant.Values.Select(Function(v) GenerateExpression(generator, v))))
End If
Case TypedConstantKind.Type
If Not TypeOf typedConstant.Value Is ITypeSymbol Then
Return GenerateNothingLiteral()
End If
Return SyntaxFactory.GetTypeExpression(DirectCast(typedConstant.Value, ITypeSymbol).GenerateTypeSyntax())
Case Else
Return GenerateNothingLiteral()
End Select
End Function
Friend Function GenerateExpression(generator As SyntaxGenerator, type As ITypeSymbol, value As Object, canUseFieldReference As Boolean) As ExpressionSyntax
If value IsNot Nothing Then
If type.IsNullable() Then
' If the type of the argument is T?, then the type of the supplied default value can either be T
' (e.g. Optional x As Integer? = 5) or it can be T? (e.g. Optional x as SomeStruct? = Nothing). The
' below statement handles the case where the type of the supplied default value is T.
Return GenerateExpression(generator, DirectCast(type, INamedTypeSymbol).TypeArguments(0), value, canUseFieldReference)
ElseIf type?.TypeKind = TypeKind.Enum Then
Return DirectCast(VisualBasicFlagsEnumGenerator.Instance.CreateEnumConstantValue(
generator, DirectCast(type, INamedTypeSymbol), value), ExpressionSyntax)
End If
End If
Return GenerateNonEnumValueExpression(type, value, canUseFieldReference)
End Function
Friend Function GenerateNonEnumValueExpression(type As ITypeSymbol, value As Object, canUseFieldReference As Boolean) As ExpressionSyntax
If TypeOf value Is Boolean Then
Dim boolValue = DirectCast(value, Boolean)
If boolValue Then
Return SyntaxFactory.TrueLiteralExpression(SyntaxFactory.Token(SyntaxKind.TrueKeyword))
Else
Return SyntaxFactory.FalseLiteralExpression(SyntaxFactory.Token(SyntaxKind.FalseKeyword))
End If
ElseIf TypeOf value Is String Then
Return GenerateStringLiteralExpression(type, DirectCast(value, String))
ElseIf TypeOf value Is Char Then
Return GenerateCharLiteralExpression(DirectCast(value, Char))
ElseIf TypeOf value Is SByte Then
Return GenerateIntegralLiteralExpression(type, SpecialType.System_SByte, DirectCast(value, SByte), canUseFieldReference, LiteralSpecialValues.SByteSpecialValues, Function(x) x < 0, Function(x) -x, "128")
ElseIf TypeOf value Is Short Then
Return GenerateIntegralLiteralExpression(type, SpecialType.System_Int16, DirectCast(value, Short), canUseFieldReference, LiteralSpecialValues.Int16SpecialValues, Function(x) x < 0, Function(x) -x, "32768")
ElseIf TypeOf value Is Integer Then
Return GenerateIntegralLiteralExpression(type, SpecialType.System_Int32, DirectCast(value, Integer), canUseFieldReference, LiteralSpecialValues.Int32SpecialValues, Function(x) x < 0, Function(x) -x, "2147483648")
ElseIf TypeOf value Is Long Then
Return GenerateIntegralLiteralExpression(type, SpecialType.System_Int64, DirectCast(value, Long), canUseFieldReference, LiteralSpecialValues.Int64SpecialValues, Function(x) x < 0, Function(x) -x, "9223372036854775808")
ElseIf TypeOf value Is Byte Then
Return GenerateNonNegativeIntegralLiteralExpression(type, SpecialType.System_Byte, DirectCast(value, Byte), canUseFieldReference, LiteralSpecialValues.ByteSpecialValues)
ElseIf TypeOf value Is UShort Then
Return GenerateNonNegativeIntegralLiteralExpression(type, SpecialType.System_UInt16, DirectCast(value, UShort), canUseFieldReference, LiteralSpecialValues.UInt16SpecialValues)
ElseIf TypeOf value Is UInteger Then
Return GenerateNonNegativeIntegralLiteralExpression(type, SpecialType.System_UInt32, DirectCast(value, UInteger), canUseFieldReference, LiteralSpecialValues.UInt32SpecialValues)
ElseIf TypeOf value Is ULong Then
Return GenerateNonNegativeIntegralLiteralExpression(type, SpecialType.System_UInt64, DirectCast(value, ULong), canUseFieldReference, LiteralSpecialValues.UInt64SpecialValues)
ElseIf TypeOf value Is Single Then
Return GenerateSingleLiteralExpression(type, DirectCast(value, Single), canUseFieldReference)
ElseIf TypeOf value Is Double Then
Return GenerateDoubleLiteralExpression(type, DirectCast(value, Double), canUseFieldReference)
ElseIf TypeOf value Is Decimal Then
Return GenerateDecimalLiteralExpression(type, DirectCast(value, Decimal), canUseFieldReference)
ElseIf TypeOf value Is DateTime Then
Return GenerateDateLiteralExpression(DirectCast(value, DateTime))
Else
Return GenerateNothingLiteral()
End If
End Function
Private Function GenerateNothingLiteral() As ExpressionSyntax
Return SyntaxFactory.NothingLiteralExpression(SyntaxFactory.Token(SyntaxKind.NothingKeyword))
End Function
Private Function GenerateDateLiteralExpression(value As Date) As ExpressionSyntax
Dim literal = SymbolDisplay.FormatPrimitive(value, quoteStrings:=False, useHexadecimalNumbers:=False)
Return SyntaxFactory.DateLiteralExpression(
SyntaxFactory.DateLiteralToken(literal, value))
End Function
Private Function GenerateStringLiteralExpression(type As ITypeSymbol, value As String) As ExpressionSyntax
Dim pieces = StringPiece.Split(value)
If pieces.Count = 0 Then
Return SyntaxFactory.StringLiteralExpression(SyntaxFactory.StringLiteralToken(s_doubleQuote & s_doubleQuote, String.Empty))
End If
If pieces.Count = 1 AndAlso pieces(0).Kind = StringPieceKind.NonPrintable Then
If Not IsSpecialType(type, SpecialType.System_String) Then
Return SyntaxFactory.PredefinedCastExpression(SyntaxFactory.Token(SyntaxKind.CStrKeyword), pieces(0).GenerateExpression())
End If
End If
Dim expression As ExpressionSyntax = Nothing
For Each piece In pieces
Dim subExpression = piece.GenerateExpression()
If expression Is Nothing Then
expression = subExpression
Else
expression = SyntaxFactory.ConcatenateExpression(expression, subExpression)
End If
Next
Return expression
End Function
Private Function GenerateMemberAccessExpression(ParamArray names As String()) As MemberAccessExpressionSyntax
Dim expression As ExpressionSyntax = SyntaxFactory.GlobalName()
For Each name In names
expression = SyntaxFactory.SimpleMemberAccessExpression(
expression,
SyntaxFactory.Token(SyntaxKind.DotToken),
SyntaxFactory.IdentifierName(name))
Next
Return DirectCast(expression, MemberAccessExpressionSyntax).WithAdditionalAnnotations(Simplifier.Annotation)
End Function
Private Function GenerateChrWExpression(c As Char) As InvocationExpressionSyntax
Dim access = GenerateMemberAccessExpression("Microsoft", "VisualBasic", "Strings", "ChrW")
Dim value = AscW(c)
Dim argument = SyntaxFactory.SimpleArgument(
SyntaxFactory.NumericLiteralExpression(
SyntaxFactory.IntegerLiteralToken(value.ToString(Nothing, CultureInfo.InvariantCulture), LiteralBase.Decimal, TypeCharacter.None, CULng(value))))
Dim invocation = SyntaxFactory.InvocationExpression(
access,
SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(Of ArgumentSyntax)(argument)))
Return invocation.WithAdditionalAnnotations(Simplifier.Annotation)
End Function
Private Function GenerateNonNegativeIntegralLiteralExpression(Of TStructure As IEquatable(Of TStructure))(
type As ITypeSymbol,
specialType As SpecialType,
value As TStructure,
canUseFieldReference As Boolean,
specialValues As IEnumerable(Of KeyValuePair(Of TStructure, String))) As ExpressionSyntax
Return GenerateIntegralLiteralExpression(
type, specialType, value, canUseFieldReference, specialValues,
Function(v) False,
Function(v)
Throw New InvalidOperationException()
End Function,
Nothing)
End Function
Private Function GenerateIntegralLiteralExpression(Of TStructure As IEquatable(Of TStructure))(
type As ITypeSymbol,
specialType As SpecialType,
value As TStructure,
canUseFieldReference As Boolean,
specialValues As IEnumerable(Of KeyValuePair(Of TStructure, String)),
isNegative As Func(Of TStructure, Boolean),
negate As Func(Of TStructure, TStructure),
integerMinValueString As String) As ExpressionSyntax
If canUseFieldReference Then
Dim field = GenerateFieldReference(specialType, value, specialValues)
If field IsNot Nothing Then
Return field
End If
End If
Dim negative = isNegative(value)
Dim nonNegativeValue = If(negative,
negate(value),
value)
Dim typeSuffix As TypeCharacter = TypeCharacter.None
Dim suffix As String = String.Empty
DetermineSuffix(type, nonNegativeValue, typeSuffix, suffix)
Dim literal = If(negative AndAlso nonNegativeValue.Equals(value),
integerMinValueString,
DirectCast(nonNegativeValue, IFormattable).ToString(Nothing, CultureInfo.InvariantCulture) & suffix)
Dim expression As ExpressionSyntax = SyntaxFactory.NumericLiteralExpression(SyntaxFactory.IntegerLiteralToken(
literal, LiteralBase.Decimal, typeSuffix,
IntegerUtilities.ToUInt64(nonNegativeValue)))
If negative Then
expression = SyntaxFactory.UnaryMinusExpression(expression)
End If
If TypeOf value Is Byte AndAlso Not IsSpecialType(type, SpecialType.System_Byte) Then
Return SyntaxFactory.PredefinedCastExpression(SyntaxFactory.Token(SyntaxKind.CByteKeyword), expression)
ElseIf TypeOf value Is SByte AndAlso Not IsSpecialType(type, SpecialType.System_SByte) Then
Return SyntaxFactory.PredefinedCastExpression(SyntaxFactory.Token(SyntaxKind.CSByteKeyword), expression)
End If
Return expression
End Function
Private Sub DetermineSuffix(type As ITypeSymbol,
value As Object,
ByRef typeSuffix As TypeCharacter,
ByRef suffix As String)
If TypeOf value Is Short AndAlso Not IsSpecialType(type, SpecialType.System_Int16) Then
typeSuffix = TypeCharacter.ShortLiteral
suffix = "S"
ElseIf TypeOf value Is Long AndAlso Not IsSpecialType(type, SpecialType.System_Int64) Then
typeSuffix = TypeCharacter.LongLiteral
suffix = "L"
ElseIf TypeOf value Is Decimal Then
Dim d = DirectCast(value, Decimal)
Dim scale = d.GetScale()
Dim typeIsNotDecimal = Not IsSpecialType(type, SpecialType.System_Decimal)
Dim scaleIsNotZero = scale <> 0
Dim valueIsOutOfRange = d <= Long.MinValue OrElse d > Long.MaxValue
If typeIsNotDecimal OrElse
scaleIsNotZero OrElse
valueIsOutOfRange Then
typeSuffix = TypeCharacter.DecimalLiteral
suffix = "D"
End If
ElseIf TypeOf value Is UShort AndAlso Not IsSpecialType(type, SpecialType.System_UInt16) Then
typeSuffix = TypeCharacter.UShortLiteral
suffix = "US"
ElseIf TypeOf value Is UInteger AndAlso Not IsSpecialType(type, SpecialType.System_UInt32) Then
typeSuffix = TypeCharacter.UIntegerLiteral
suffix = "UI"
ElseIf TypeOf value Is ULong Then
Dim d = DirectCast(value, ULong)
Dim typeIsNotULong = Not IsSpecialType(type, SpecialType.System_UInt64)
Dim valueIsOutOfRange = d > Long.MaxValue
If typeIsNotULong OrElse
valueIsOutOfRange Then
typeSuffix = TypeCharacter.ULongLiteral
suffix = "UL"
End If
ElseIf TypeOf value Is Single AndAlso Not IsSpecialType(type, SpecialType.System_Single) Then
typeSuffix = TypeCharacter.SingleLiteral
suffix = "F"
ElseIf TypeOf value Is Double AndAlso Not IsSpecialType(type, SpecialType.System_Double) Then
typeSuffix = TypeCharacter.DoubleLiteral
suffix = "R"
End If
End Sub
Private Function GenerateDoubleLiteralExpression(type As ITypeSymbol,
value As Double,
canUseFieldReference As Boolean) As ExpressionSyntax
If Not canUseFieldReference Then
If Double.IsNaN(value) Then
Return SyntaxFactory.DivideExpression(
GenerateFloatLiteral(0.0, "0.0"),
GenerateFloatLiteral(0.0, "0.0"))
ElseIf Double.IsPositiveInfinity(value) Then
Return SyntaxFactory.DivideExpression(
GenerateFloatLiteral(1.0, "1.0"),
GenerateFloatLiteral(0.0, "0.0"))
ElseIf (Double.IsNegativeInfinity(value)) Then
Return SyntaxFactory.DivideExpression(
SyntaxFactory.UnaryMinusExpression(GenerateFloatLiteral(1.0, "1.0")),
GenerateFloatLiteral(0.0, "0.0"))
End If
End If
Return GenerateFloatLiteralExpression(
type, SpecialType.System_Double, value, canUseFieldReference,
LiteralSpecialValues.DoubleSpecialValues, Function(t) t < 0, Function(t) -t)
End Function
Private Function GenerateSingleLiteralExpression(
type As ITypeSymbol,
value As Single,
canUseFieldReference As Boolean) As ExpressionSyntax
If Not canUseFieldReference Then
If Double.IsNaN(value) Then
Return SyntaxFactory.DivideExpression(
GenerateFloatLiteral(0.0, "0.0F"),
GenerateFloatLiteral(0.0, "0.0F"))
ElseIf Double.IsPositiveInfinity(value) Then
Return SyntaxFactory.DivideExpression(
GenerateFloatLiteral(1.0, "1.0F"),
GenerateFloatLiteral(0.0, "0.0F"))
ElseIf (Double.IsNegativeInfinity(value)) Then
Return SyntaxFactory.DivideExpression(
SyntaxFactory.UnaryMinusExpression(GenerateFloatLiteral(1.0, "1.0F")),
GenerateFloatLiteral(0.0, "0.0F"))
End If
End If
Return GenerateFloatLiteralExpression(
type, SpecialType.System_Single, value, canUseFieldReference,
LiteralSpecialValues.SingleSpecialValues, Function(t) t < 0, Function(t) -t)
End Function
Private Function GenerateFloatLiteralExpression(Of TStructure)(
type As ITypeSymbol,
specialType As SpecialType,
value As TStructure,
canUseFieldReference As Boolean,
specialValues As IEnumerable(Of KeyValuePair(Of TStructure, String)),
isNegative As Func(Of TStructure, Boolean),
negate As Func(Of TStructure, TStructure)) As ExpressionSyntax
If canUseFieldReference Then
Dim field = GenerateFieldReference(specialType, value, specialValues)
If field IsNot Nothing Then
Return field
End If
End If
Dim negative = isNegative(value)
If negative Then
value = negate(value)
End If
Dim typeSuffix As TypeCharacter = TypeCharacter.None
Dim suffix As String = String.Empty
DetermineSuffix(type, value, typeSuffix, suffix)
Dim literal = DirectCast(value, IFormattable).ToString("R", CultureInfo.InvariantCulture) & suffix
Dim literalSyntax As ExpressionSyntax = GenerateFloatLiteral(Convert.ToDouble(value), literal, typeSuffix)
Return If(negative, SyntaxFactory.UnaryMinusExpression(literalSyntax), literalSyntax)
End Function
Private Function GenerateFloatLiteral(value As Double,
literal As String,
Optional typeSuffix As TypeCharacter = TypeCharacter.None) As LiteralExpressionSyntax
Return SyntaxFactory.NumericLiteralExpression(SyntaxFactory.FloatingLiteralToken(
literal, typeSuffix, value))
End Function
Private Function GenerateCharLiteralExpression(c As Char) As ExpressionSyntax
Dim pieces = StringPiece.Split(c.ToString())
Dim piece = pieces(0)
If piece.Kind = StringPieceKind.Normal Then
Return SyntaxFactory.CharacterLiteralExpression(SyntaxFactory.CharacterLiteralToken(
SymbolDisplay.FormatPrimitive(c, quoteStrings:=True, useHexadecimalNumbers:=False), c))
End If
Return GenerateChrWExpression(c)
End Function
Private Function GenerateDecimalLiteralExpression(type As ITypeSymbol, value As Decimal, canUseFieldReference As Boolean) As ExpressionSyntax
If canUseFieldReference Then
Dim field = GenerateFieldReference(SpecialType.System_Decimal, value, LiteralSpecialValues.DecimalSpecialValues)
If field IsNot Nothing Then
Return field
End If
End If
Dim typeSuffix As TypeCharacter = TypeCharacter.None
Dim suffix As String = String.Empty
DetermineSuffix(type, value, typeSuffix, suffix)
Dim literal = value.ToString(Nothing, CultureInfo.InvariantCulture) & suffix
Return SyntaxFactory.NumericLiteralExpression(SyntaxFactory.DecimalLiteralToken(literal, typeSuffix, value))
End Function
Private Function AddSpecialTypeAnnotation(type As SpecialType, expression As MemberAccessExpressionSyntax) As MemberAccessExpressionSyntax
If SpecialType.None <> type Then
Return expression.WithAdditionalAnnotations(SpecialTypeAnnotation.Create(type))
End If
Return expression
End Function
Private Function GenerateFieldReference(Of TStructure)(type As SpecialType,
value As Object,
specialValues As IEnumerable(Of KeyValuePair(Of TStructure, String))) As MemberAccessExpressionSyntax
For Each specialValue In specialValues
If specialValue.Key.Equals(value) Then
Dim memberAccess = AddSpecialTypeAnnotation(type, GenerateMemberAccessExpression("System", GetType(TStructure).Name))
Return SyntaxFactory.SimpleMemberAccessExpression(memberAccess, SyntaxFactory.Token(SyntaxKind.DotToken), SyntaxFactory.IdentifierName(specialValue.Value)) _
.WithAdditionalAnnotations(Simplifier.Annotation)
End If
Next
Return Nothing
End Function
End Module
End Namespace
|