File: Symbols\Attributes\SourceAttributeData.vb
Web Access
Project: src\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' 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
Imports System.Collections.Generic
Imports System.Collections.Immutable
Imports Microsoft.Cci
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.Emit
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports System.Reflection.Metadata
 
Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
 
    Friend NotInheritable Class SourceAttributeData
        Inherits VisualBasicAttributeData
 
        Private ReadOnly _compilation As VisualBasicCompilation
        Private ReadOnly _attributeClass As NamedTypeSymbol ' TODO - Remove attribute class. It is available from the constructor.
        Private ReadOnly _attributeConstructor As MethodSymbol
        Private ReadOnly _constructorArguments As ImmutableArray(Of TypedConstant)
        Private ReadOnly _namedArguments As ImmutableArray(Of KeyValuePair(Of String, TypedConstant))
        Private ReadOnly _isConditionallyOmitted As Boolean
        Private ReadOnly _hasErrors As Boolean
        Private ReadOnly _applicationNode As SyntaxReference
 
        Friend Sub New(ByVal compilation As VisualBasicCompilation,
                       ByVal applicationNode As SyntaxReference,
                       ByVal attrClass As NamedTypeSymbol,
                       ByVal attrMethod As MethodSymbol,
                       ByVal constructorArgs As ImmutableArray(Of TypedConstant),
                       ByVal namedArgs As ImmutableArray(Of KeyValuePair(Of String, TypedConstant)),
                       ByVal isConditionallyOmitted As Boolean,
                       ByVal hasErrors As Boolean)
            Debug.Assert(compilation IsNot Nothing)
            Debug.Assert(applicationNode IsNot Nothing)
            Debug.Assert(attrMethod IsNot Nothing OrElse hasErrors)
 
            Me._compilation = compilation
            Me._applicationNode = applicationNode
            Me._attributeClass = attrClass
            Me._attributeConstructor = attrMethod
            Me._constructorArguments = constructorArgs.NullToEmpty()
            Me._namedArguments = If(namedArgs.IsDefault, ImmutableArray.Create(Of KeyValuePair(Of String, TypedConstant))(), namedArgs)
            Me._isConditionallyOmitted = isConditionallyOmitted
            Me._hasErrors = hasErrors
        End Sub
 
        Public Overrides ReadOnly Property AttributeClass As NamedTypeSymbol
            Get
                Return _attributeClass
            End Get
        End Property
 
        Public Overrides ReadOnly Property AttributeConstructor As MethodSymbol
            Get
                Return _attributeConstructor
            End Get
        End Property
 
        Public Overrides ReadOnly Property ApplicationSyntaxReference As SyntaxReference
            Get
                Return _applicationNode
            End Get
        End Property
 
        Protected Overrides ReadOnly Property CommonConstructorArguments As ImmutableArray(Of TypedConstant)
            Get
                Return _constructorArguments
            End Get
        End Property
 
        Protected Overrides ReadOnly Property CommonNamedArguments As ImmutableArray(Of KeyValuePair(Of String, TypedConstant))
            Get
                Return _namedArguments
            End Get
        End Property
 
        Friend Overrides ReadOnly Property IsConditionallyOmitted As Boolean
            Get
                Return _isConditionallyOmitted
            End Get
        End Property
 
        Friend Function WithOmittedCondition(isConditionallyOmitted As Boolean) As SourceAttributeData
            If Me.IsConditionallyOmitted = isConditionallyOmitted Then
                Return Me
            End If
 
            Return New SourceAttributeData(Me._compilation,
                                           Me.ApplicationSyntaxReference,
                                           Me.AttributeClass,
                                           Me.AttributeConstructor,
                                           Me.CommonConstructorArguments,
                                           Me.CommonNamedArguments,
                                           isConditionallyOmitted,
                                           Me.HasErrors)
        End Function
 
        Friend Overrides ReadOnly Property HasErrors As Boolean
            Get
                Return _hasErrors
            End Get
        End Property
 
        Friend Overrides ReadOnly Property ErrorInfo As DiagnosticInfo
            Get
                Return Nothing ' Binder reports errors
            End Get
        End Property
 
        ''' <summary>
        ''' This method finds an attribute by metadata name and signature. The algorithm for signature matching is similar to the one
        ''' in Module.GetTargetAttributeSignatureIndex. Note, the signature matching is limited to primitive types
        ''' and System.Type.  It will not match an arbitrary signature but it is sufficient to match the signatures of the current set of
        ''' well known attributes.
        ''' </summary>
        ''' <param name="description">Attribute to match.</param>
        Friend Overrides Function GetTargetAttributeSignatureIndex(description As AttributeDescription) As Integer
            Return GetTargetAttributeSignatureIndex(_compilation, AttributeClass, AttributeConstructor, description)
        End Function
 
        Friend Overloads Shared Function GetTargetAttributeSignatureIndex(compilation As VisualBasicCompilation, attributeClass As NamedTypeSymbol, attributeConstructor As MethodSymbol, description As AttributeDescription) As Integer
            If Not IsTargetAttribute(attributeClass, description.Namespace, description.Name, description.MatchIgnoringCase) Then
                Return -1
            End If
 
            Dim lazySystemType As TypeSymbol = Nothing
 
            Dim ctor = attributeConstructor
            ' Ensure that the attribute data really has a constructor before comparing the signature.
            If ctor Is Nothing Then
                Return -1
            End If
 
            Dim parameters = ctor.Parameters
            Dim foundMatch = False
 
            For i = 0 To description.Signatures.Length - 1
                Dim targetSignature = description.Signatures(i)
                If targetSignature(0) <> SignatureAttributes.Instance Then
                    Continue For
                End If
 
                Dim parameterCount = targetSignature(1)
                If parameterCount <> parameters.Length Then
                    Continue For
                End If
 
                If CType(targetSignature(2), SignatureTypeCode) <> SignatureTypeCode.Void Then
                    Continue For
                End If
 
                foundMatch = (targetSignature.Length = 3)
                Dim k = 0
                For j = 3 To targetSignature.Length - 1
                    If k >= parameters.Length Then
                        Exit For
                    End If
 
                    Dim parameterType As TypeSymbol = parameters(k).Type
                    Dim specType = parameterType.GetEnumUnderlyingTypeOrSelf.SpecialType
                    Dim targetType As Byte = targetSignature(j)
 
                    If targetType = SignatureTypeCode.TypeHandle Then
                        j += 1
 
                        If parameterType.Kind <> SymbolKind.NamedType AndAlso parameterType.Kind <> SymbolKind.ErrorType Then
                            foundMatch = False
                            Exit For
                        End If
 
                        Dim namedType = DirectCast(parameterType, NamedTypeSymbol)
                        Dim targetInfo As AttributeDescription.TypeHandleTargetInfo = AttributeDescription.TypeHandleTargets(targetSignature(j))
 
                        ' Compare name and containing symbol name. Uses HasNameQualifier
                        ' extension method to avoid string allocations.
                        If Not String.Equals(namedType.MetadataName, targetInfo.Name, StringComparison.Ordinal) OrElse
                            Not namedType.HasNameQualifier(targetInfo.Namespace, StringComparison.Ordinal) Then
                            foundMatch = False
                            Exit For
                        End If
 
                        targetType = CByte(targetInfo.Underlying)
 
                    ElseIf parameterType.IsArrayType Then
                        specType = DirectCast(parameterType, ArrayTypeSymbol).ElementType.SpecialType
 
                    End If
 
                    Select Case targetType
                        Case CByte(SignatureTypeCode.Boolean)
                            foundMatch = specType = SpecialType.System_Boolean
                            k += 1
 
                        Case CByte(SignatureTypeCode.Char)
                            foundMatch = specType = SpecialType.System_Char
                            k += 1
 
                        Case CByte(SignatureTypeCode.SByte)
                            foundMatch = specType = SpecialType.System_SByte
                            k += 1
 
                        Case CByte(SignatureTypeCode.Byte)
                            foundMatch = specType = SpecialType.System_Byte
                            k += 1
 
                        Case CByte(SignatureTypeCode.Int16)
                            foundMatch = specType = SpecialType.System_Int16
                            k += 1
 
                        Case CByte(SignatureTypeCode.UInt16)
                            foundMatch = specType = SpecialType.System_UInt16
                            k += 1
 
                        Case CByte(SignatureTypeCode.Int32)
                            foundMatch = specType = SpecialType.System_Int32
                            k += 1
 
                        Case CByte(SignatureTypeCode.UInt32)
                            foundMatch = specType = SpecialType.System_UInt32
                            k += 1
 
                        Case CByte(SignatureTypeCode.Int64)
                            foundMatch = specType = SpecialType.System_Int64
                            k += 1
 
                        Case CByte(SignatureTypeCode.UInt64)
                            foundMatch = specType = SpecialType.System_UInt64
                            k += 1
 
                        Case CByte(SignatureTypeCode.Single)
                            foundMatch = specType = SpecialType.System_Single
                            k += 1
 
                        Case CByte(SignatureTypeCode.Double)
                            foundMatch = specType = SpecialType.System_Double
                            k += 1
 
                        Case CByte(SignatureTypeCode.String)
                            foundMatch = specType = SpecialType.System_String
                            k += 1
 
                        Case CByte(SignatureTypeCode.Object)
                            foundMatch = specType = SpecialType.System_Object
                            k += 1
 
                        Case CByte(SerializationTypeCode.Type)
                            If lazySystemType Is Nothing Then
                                lazySystemType = compilation.GetWellKnownType(WellKnownType.System_Type)
                            End If
 
                            foundMatch = TypeSymbol.Equals(parameterType, lazySystemType, TypeCompareKind.ConsiderEverything)
                            k += 1
 
                        Case CByte(SignatureTypeCode.SZArray)
                            ' skip over and check the next byte
                            foundMatch = parameterType.IsArrayType
 
                        Case Else
                            Return -1
                    End Select
 
                    If Not foundMatch Then
                        Exit For
                    End If
                Next
 
                If foundMatch Then
                    Return i
                End If
            Next
 
            Debug.Assert(Not foundMatch)
            Return -1
        End Function
 
        ''' <summary>
        ''' Compares the namespace and type name with the attribute's namespace and type name.  Returns true if they are the same.
        ''' </summary>
        Friend Overrides Function IsTargetAttribute(
            namespaceName As String,
            typeName As String,
            Optional ignoreCase As Boolean = False
        ) As Boolean
            Return IsTargetAttribute(AttributeClass, namespaceName, typeName, ignoreCase)
        End Function
 
        Friend Overloads Shared Function IsTargetAttribute(
            attributeClass As NamedTypeSymbol,
            namespaceName As String,
            typeName As String,
            Optional ignoreCase As Boolean = False
        ) As Boolean
            If attributeClass.IsErrorType() AndAlso Not TypeOf attributeClass Is MissingMetadataTypeSymbol Then
                ' Can't guarantee complete name information.
                Return False
            End If
 
            Dim options As StringComparison = If(ignoreCase, StringComparison.OrdinalIgnoreCase, StringComparison.Ordinal)
            Return attributeClass.HasNameQualifier(namespaceName, options) AndAlso
                attributeClass.Name.Equals(typeName, options)
        End Function
    End Class
 
End Namespace