File: Symbols\AnonymousTypes\SynthesizedSymbols\AnonymousType_TemplateSymbol.vb
Web Access
Project: src\roslyn\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.Collections.Generic
Imports System.Collections.Immutable
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Collections
Imports Microsoft.CodeAnalysis.Emit
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Emit

Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols

    Partial Friend NotInheritable Class AnonymousTypeManager

        Private NotInheritable Class AnonymousTypeTemplateSymbol
            Inherits AnonymousTypeOrDelegateTemplateSymbol

            Private ReadOnly _properties As ImmutableArray(Of AnonymousTypePropertySymbol)
            Private ReadOnly _members As ImmutableArray(Of Symbol)
            Private ReadOnly _interfaces As ImmutableArray(Of NamedTypeSymbol)
            Friend ReadOnly HasAtLeastOneKeyField As Boolean

            Public Sub New(manager As AnonymousTypeManager,
                           typeDescr As AnonymousTypeDescriptor)
                MyBase.New(manager, typeDescr)

                Dim fieldsCount As Integer = typeDescr.Fields.Length

                Dim methodMembersBuilder = ArrayBuilder(Of Symbol).GetInstance()
                Dim otherMembersBuilder = ArrayBuilder(Of Symbol).GetInstance()

                ' The array storing property symbols to be used in 
                ' generation of constructor and other methods
                Dim propertiesArray = New AnonymousTypePropertySymbol(fieldsCount - 1) {}

                ' Anonymous types with at least one Key field are being generated slightly different
                HasAtLeastOneKeyField = False

                '  Process fields
                For fieldIndex = 0 To fieldsCount - 1

                    Dim field As AnonymousTypeField = typeDescr.Fields(fieldIndex)
                    If field.IsKey Then
                        HasAtLeastOneKeyField = True
                    End If

                    ' Add a property
                    Dim [property] As New AnonymousTypePropertySymbol(Me, field, fieldIndex, Me.TypeParameters(fieldIndex))
                    propertiesArray(fieldIndex) = [property]

                    ' Property related symbols
                    otherMembersBuilder.Add([property])
                    methodMembersBuilder.Add([property].GetMethod)
                    If [property].SetMethod IsNot Nothing Then
                        methodMembersBuilder.Add([property].SetMethod)
                    End If

                    otherMembersBuilder.Add([property].AssociatedField)
                Next

                _properties = propertiesArray.AsImmutableOrNull()

                ' Add a constructor
                methodMembersBuilder.Add(New AnonymousTypeConstructorSymbol(Me))
                ' Add 'ToString'
                methodMembersBuilder.Add(New AnonymousTypeToStringMethodSymbol(Me))

                ' Add optional members
                If HasAtLeastOneKeyField AndAlso Me.Manager.System_IEquatable_T_Equals IsNot Nothing Then

                    ' Add 'GetHashCode'
                    methodMembersBuilder.Add(New AnonymousTypeGetHashCodeMethodSymbol(Me))

                    ' Add optional 'Inherits IEquatable'
                    Dim equatableInterface As NamedTypeSymbol = Me.Manager.System_IEquatable_T.Construct(ImmutableArray.Create(Of TypeSymbol)(Me))
                    _interfaces = ImmutableArray.Create(Of NamedTypeSymbol)(equatableInterface)

                    ' Add 'IEquatable.Equals'
                    Dim method As Symbol = DirectCast(equatableInterface, SubstitutedNamedType).GetMemberForDefinition(Me.Manager.System_IEquatable_T_Equals)
                    Dim iEquatableEquals As MethodSymbol = New AnonymousType_IEquatable_EqualsMethodSymbol(Me, DirectCast(method, MethodSymbol))
                    methodMembersBuilder.Add(iEquatableEquals)

                    ' Add 'Equals'
                    methodMembersBuilder.Add(New AnonymousTypeEqualsMethodSymbol(Me, iEquatableEquals))

                Else
                    _interfaces = ImmutableArray(Of NamedTypeSymbol).Empty
                End If

                methodMembersBuilder.AddRange(otherMembersBuilder)
                otherMembersBuilder.Free()
                _members = methodMembersBuilder.ToImmutableAndFree()
            End Sub

            Friend Overrides Function GetAnonymousTypeKey() As AnonymousTypeKey
                Dim properties = _properties.SelectAsArray(Function(p) New AnonymousTypeKeyField(p.Name, isKey:=p.IsReadOnly, ignoreCase:=True))
                Return New AnonymousTypeKey(properties)
            End Function

            Friend Overrides ReadOnly Property GeneratedNamePrefix As String
                Get
                    Return GeneratedNameConstants.AnonymousTypeTemplateNamePrefix
                End Get
            End Property

            Public ReadOnly Property Properties As ImmutableArray(Of AnonymousTypePropertySymbol)
                Get
                    Return Me._properties
                End Get
            End Property

            Public Overrides Function GetMembers() As ImmutableArray(Of Symbol)
                Return _members
            End Function

            Friend Overrides Iterator Function GetFieldsToEmit() As IEnumerable(Of FieldSymbol)
                For Each m In GetMembers()
                    If m.Kind = SymbolKind.Field Then
                        Yield DirectCast(m, FieldSymbol)
                    End If
                Next
            End Function

            Friend Overrides Function MakeAcyclicBaseType(diagnostics As BindingDiagnosticBag) As NamedTypeSymbol
                Return Me.Manager.System_Object
            End Function

            Friend Overrides Function MakeAcyclicInterfaces(diagnostics As BindingDiagnosticBag) As ImmutableArray(Of NamedTypeSymbol)
                Return _interfaces
            End Function

            Public Overrides ReadOnly Property TypeKind As TypeKind
                Get
                    Return TypeKind.Class
                End Get
            End Property

            Friend Overrides ReadOnly Property IsInterface As Boolean
                Get
                    Return False
                End Get
            End Property

            Friend Overrides Sub AddSynthesizedAttributes(moduleBuilder As PEModuleBuilder, ByRef attributes As ArrayBuilder(Of VisualBasicAttributeData))
                MyBase.AddSynthesizedAttributes(moduleBuilder, attributes)

                ' Attribute: System.Runtime.CompilerServices.CompilerGeneratedAttribute()
                AddSynthesizedAttribute(attributes, Manager.Compilation.TrySynthesizeAttribute(
                    WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor))

                ' VB emits this attribute regardless of /debug settings (unlike C#, which only emits it for /debug:full)
                ' Attribute: System.Diagnostics.DebuggerDisplayAttribute("a={a}, b={b}, c={c}, ...")
                AddSynthesizedAttribute(attributes, SynthesizeDebuggerDisplayAttribute())
            End Sub

            Private Function SynthesizeDebuggerDisplayAttribute() As SynthesizedAttributeData
                ' VB doesn't allow empty anon types
                Debug.Assert(Me.Properties.Length > 0)

                Dim builder = PooledStringBuilder.GetInstance()
                Dim sb = builder.Builder
                Dim displayCount As Integer = Math.Min(Me.Properties.Length, 4)

                For fieldIndex = 0 To displayCount - 1
                    Dim fieldName As String = Me.Properties(fieldIndex).Name
                    If fieldIndex > 0 Then
                        sb.Append(", ")
                    End If

                    sb.Append(fieldName)
                    sb.Append("={")
                    sb.Append(fieldName)
                    sb.Append("}")
                Next

                If Me.Properties.Length > displayCount Then
                    sb.Append(", ...")
                End If

                Return Manager.Compilation.TrySynthesizeAttribute(
                    WellKnownMember.System_Diagnostics_DebuggerDisplayAttribute__ctor,
                    ImmutableArray.Create(New TypedConstant(Manager.System_String, TypedConstantKind.Primitive, builder.ToStringAndFree())))
            End Function

        End Class
    End Class
End Namespace