File: Symbols\Metadata\PE\PENamespaceSymbol.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.Collections.Generic
Imports System.Collections.Immutable
Imports System.Collections.ObjectModel
Imports System.Reflection.Metadata
Imports System.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
 
Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
 
    ''' <summary>
    ''' The base class to represent a namespace imported from a PE/module.
    ''' Namespaces that differ only by casing in name are merged.
    ''' </summary>
    Friend MustInherit Class PENamespaceSymbol
        Inherits PEOrSourceOrMergedNamespaceSymbol
 
        ''' <summary>
        ''' A map of namespaces immediately contained within this namespace 
        ''' grouped by their name (case-insensitively).
        ''' </summary>
        Protected m_lazyMembers As Dictionary(Of String, ImmutableArray(Of Symbol))
 
        ''' <summary>
        ''' A map of types immediately contained within this namespace 
        ''' grouped by their name (case-insensitively).
        ''' </summary>
        Protected m_lazyTypes As Dictionary(Of String, ImmutableArray(Of PENamedTypeSymbol))
 
        ''' <summary>
        ''' A map of NoPia local types immediately contained in this assembly.
        ''' Maps fully-qualified type name to the row id.
        ''' </summary>
        Private _lazyNoPiaLocalTypes As Dictionary(Of String, TypeDefinitionHandle)
 
        ' Lazily filled in collection of all contained modules.
        Private _lazyModules As ImmutableArray(Of NamedTypeSymbol)
 
        ' Lazily filled in collection of all contained types.
        Private _lazyFlattenedTypes As ImmutableArray(Of NamedTypeSymbol)
 
        ' Lazily filled in collection of all contained namespaces and types.
        Private _lazyFlattenedNamespacesAndTypes As ImmutableArray(Of Symbol)
 
        Friend NotOverridable Overrides ReadOnly Property Extent As NamespaceExtent
            Get
                Return New NamespaceExtent(Me.ContainingPEModule)
            End Get
        End Property
 
        Public Overrides Function GetModuleMembers() As ImmutableArray(Of NamedTypeSymbol)
            ' Since this gets called a LOT during binding, it's worth caching the result.
 
            If _lazyModules.IsDefault Then
                ' We have to read all the types to discover which ones are modules, so I'm not 
                ' sure there is any better strategy on first call then getting all type members
                ' and filtering them.
                ' Ordered.
                Dim modules = GetTypeMembers().WhereAsArray(Function(t) t.TypeKind = TYPEKIND.Module)
                ImmutableInterlocked.InterlockedCompareExchange(_lazyModules, modules, Nothing)
            End If
 
            Return _lazyModules
        End Function
 
        Public Overrides Function GetModuleMembers(name As String) As ImmutableArray(Of NamedTypeSymbol)
            ' This is not called during binding, so caching isn't very critical.
            Return GetTypeMembers(name).WhereAsArray(Function(t) t.TypeKind = TYPEKIND.Module)
        End Function
 
        Public NotOverridable Overloads Overrides Function GetMembers() As ImmutableArray(Of Symbol)
            If _lazyFlattenedNamespacesAndTypes.IsDefault Then
                EnsureAllMembersLoaded()
                ImmutableInterlocked.InterlockedExchange(_lazyFlattenedNamespacesAndTypes, m_lazyMembers.Flatten())
            End If
 
            Return _lazyFlattenedNamespacesAndTypes
        End Function
 
        Friend Overrides ReadOnly Property EmbeddedSymbolKind As EmbeddedSymbolKind
            Get
                Return EmbeddedSymbolKind.None
            End Get
        End Property
 
        Public NotOverridable Overloads Overrides Function GetMembers(name As String) As ImmutableArray(Of Symbol)
            EnsureAllMembersLoaded()
 
            Dim m As ImmutableArray(Of Symbol) = Nothing
 
            If m_lazyMembers.TryGetValue(name, m) Then
                Return m
            End If
 
            Return ImmutableArray(Of Symbol).Empty
        End Function
 
        Public NotOverridable Overloads Overrides Function GetTypeMembers() As ImmutableArray(Of NamedTypeSymbol)
            Dim result = _lazyFlattenedTypes
            If Not result.IsDefault Then
                Return result
            End If
 
            EnsureAllMembersLoaded()
            result = StaticCast(Of NamedTypeSymbol).From(m_lazyTypes.Flatten())
 
            _lazyFlattenedTypes = result
            Return result
        End Function
 
        Public NotOverridable Overloads Overrides Function GetTypeMembers(name As String) As ImmutableArray(Of NamedTypeSymbol)
            EnsureAllMembersLoaded()
 
            Dim t As ImmutableArray(Of PENamedTypeSymbol) = Nothing
 
            If m_lazyTypes.TryGetValue(name, t) Then
                Return StaticCast(Of NamedTypeSymbol).From(t)
            End If
 
            Return ImmutableArray(Of NamedTypeSymbol).Empty
        End Function
 
        Public Overloads Overrides Function GetTypeMembers(name As String, arity As Integer) As ImmutableArray(Of NamedTypeSymbol)
            Return GetTypeMembers(name).WhereAsArray(Function(type, arity_) type.Arity = arity_, arity)
        End Function
 
        Public NotOverridable Overrides ReadOnly Property Locations As ImmutableArray(Of Location)
            Get
                Return StaticCast(Of Location).From(ContainingPEModule.MetadataLocation)
            End Get
        End Property
 
        Public Overrides ReadOnly Property DeclaringSyntaxReferences As ImmutableArray(Of SyntaxReference)
            Get
                Return ImmutableArray(Of SyntaxReference).Empty
            End Get
        End Property
 
        ''' <summary>
        ''' Returns PEModuleSymbol containing the namespace.
        ''' </summary>
        ''' <returns>PEModuleSymbol containing the namespace.</returns>
        Friend MustOverride ReadOnly Property ContainingPEModule As PEModuleSymbol
 
        Protected MustOverride Sub EnsureAllMembersLoaded()
 
        ''' <summary>
        ''' Initializes m_Namespaces and m_Types maps with information about 
        ''' namespaces and types immediately contained within this namespace.
        ''' </summary>
        ''' <param name="typesByNS">
        ''' The sequence of groups of TypeDef row ids for types contained within the namespace, 
        ''' recursively including those from nested namespaces. The row ids must be grouped by the 
        ''' fully-qualified namespace name in case-sensitive manner. There could be multiple groups 
        ''' for each fully-qualified namespace name. The groups must be sorted by their key in 
        ''' case-insensitive manner. Empty string must be used as namespace name for types 
        ''' immediately contained within Global namespace. Therefore, all types in THIS namespace, 
        ''' if any, must be in several first IGroupings.
        ''' </param>
        Protected Sub LoadAllMembers(typesByNS As IEnumerable(Of IGrouping(Of String, TypeDefinitionHandle)))
            Debug.Assert(typesByNS IsNot Nothing)
 
            ' A sequence of TypeDef handles for types immediately contained within this namespace.
            Dim nestedTypes As IEnumerable(Of IGrouping(Of String, TypeDefinitionHandle)) = Nothing
 
            ' A sequence with information about namespaces immediately contained within this namespace.
            ' For each pair:
            '    Key - contains simple name of a child namespace.
            '    Value - contains a sequence similar to the one passed to this function, but
            '            calculated for the child namespace. 
            Dim nestedNamespaces As IEnumerable(Of KeyValuePair(Of String, IEnumerable(Of IGrouping(Of String, TypeDefinitionHandle)))) = Nothing
 
            'TODO: Perhaps there is a cheaper way to calculate the length of the name without actually building it with ToDisplayString.
            Dim isGlobalNamespace As Boolean = Me.IsGlobalNamespace
 
            MetadataHelpers.GetInfoForImmediateNamespaceMembers(
                isGlobalNamespace,
                If(isGlobalNamespace, 0, ToDisplayString(SymbolDisplayFormat.QualifiedNameOnlyFormat).Length),
                typesByNS,
                CaseInsensitiveComparison.Comparer,
                nestedTypes, nestedNamespaces)
 
            LazyInitializeTypes(nestedTypes)
 
            LazyInitializeNamespaces(nestedNamespaces)
        End Sub
 
        ''' <summary>
        ''' Create symbols for nested namespaces and initialize m_Namespaces map.
        ''' </summary>
        Private Sub LazyInitializeNamespaces(
            childNamespaces As IEnumerable(Of KeyValuePair(Of String, IEnumerable(Of IGrouping(Of String, TypeDefinitionHandle))))
        )
            If m_lazyMembers Is Nothing Then
 
                Dim members As New Dictionary(Of String, ImmutableArray(Of Symbol))(CaseInsensitiveComparison.Comparer)
 
                ' Add namespaces
                For Each child In childNamespaces
                    Dim ns = New PENestedNamespaceSymbol(child.Key, Me, child.Value)
                    members.Add(ns.Name, ImmutableArray.Create(Of Symbol)(ns))
                Next
 
                ' Merge in the types
 
                For Each typeSymbols As ImmutableArray(Of PENamedTypeSymbol) In m_lazyTypes.Values
                    Dim name = typeSymbols(0).Name
                    Dim symbols As ImmutableArray(Of Symbol) = Nothing
 
                    If Not members.TryGetValue(name, symbols) Then
                        members.Add(name, StaticCast(Of Symbol).From(typeSymbols))
                    Else
                        members(name) = symbols.Concat(StaticCast(Of Symbol).From(typeSymbols))
                    End If
                Next
 
                Interlocked.CompareExchange(m_lazyMembers, members, Nothing)
            End If
        End Sub
 
        ''' <summary>
        ''' Create symbols for nested types and initialize m_Types map.
        ''' </summary>
        Private Sub LazyInitializeTypes(typeGroups As IEnumerable(Of IGrouping(Of String, TypeDefinitionHandle)))
 
            If m_lazyTypes Is Nothing Then
 
                Dim moduleSymbol = ContainingPEModule
                Dim children = ArrayBuilder(Of PENamedTypeSymbol).GetInstance()
                Dim skipCheckForPiaType = Not moduleSymbol.Module.ContainsNoPiaLocalTypes()
                Dim noPiaLocalTypes As Dictionary(Of String, TypeDefinitionHandle) = Nothing
                Dim isGlobal = Me.IsGlobalNamespace
 
                For Each g In typeGroups
                    For Each t In g
                        If skipCheckForPiaType OrElse Not moduleSymbol.Module.IsNoPiaLocalType(t) Then
                            Dim type = If(isGlobal,
                                          New PENamedTypeSymbol(moduleSymbol, Me, t),
                                          New PENamedTypeSymbolWithEmittedNamespaceName(moduleSymbol, Me, t, g.Key))
                            children.Add(type)
                        Else
                            ' The dictionary of NoPIA local types must be indexed by fully-qualified names
                            ' (namespace + type name), and key comparison must be case-sensitive. The
                            ' reason is this PENamespaceSymbol is a merged namespace of all namespaces
                            ' from the same module in metadata but with potentially different casing.
                            ' When resolving a NoPIA type, the casing must match however, so it is not
                            ' sufficient to simply use the type name. In C#, namespaces with different
                            ' casing are not merged so type name is sufficient there.
                            Try
                                Dim typeDefName As String = moduleSymbol.Module.GetTypeDefNameOrThrow(t)
 
                                If noPiaLocalTypes Is Nothing Then
                                    noPiaLocalTypes = New Dictionary(Of String, TypeDefinitionHandle)()
                                End If
                                Dim qualifiedName = MetadataHelpers.BuildQualifiedName(g.Key, typeDefName)
                                noPiaLocalTypes(qualifiedName) = t
                            Catch mrEx As BadImageFormatException
                            End Try
                        End If
                    Next
                Next
 
                Dim typesDict As Dictionary(Of String, ImmutableArray(Of PENamedTypeSymbol)) =
                    children.ToDictionary(Function(c) c.Name, CaseInsensitiveComparison.Comparer)
                children.Free()
 
                If _lazyNoPiaLocalTypes Is Nothing Then
                    Interlocked.CompareExchange(_lazyNoPiaLocalTypes, noPiaLocalTypes, Nothing)
                End If
 
                If Interlocked.CompareExchange(m_lazyTypes, typesDict, Nothing) Is Nothing Then
                    ' Build cache of TypeDef Tokens
                    ' Potentially this can be done in the background.
                    moduleSymbol.OnNewTypeDeclarationsLoaded(typesDict)
                End If
            End If
        End Sub
 
        ''' <summary>
        ''' For test purposes only.
        ''' </summary>
        Friend ReadOnly Property AreTypesLoaded As Boolean
            Get
                Return m_lazyTypes IsNot Nothing
            End Get
        End Property
 
        ''' <summary>
        ''' Return the set of types that should be checked for presence of extension methods in order to build
        ''' a map of extension methods for the namespace. 
        ''' </summary>
        Friend Overrides ReadOnly Property TypesToCheckForExtensionMethods As ImmutableArray(Of NamedTypeSymbol)
            Get
                If ContainingPEModule.MightContainExtensionMethods Then
                    ' Note that we are using GetTypeMembers rather than GetModuleMembers because non-Modules imported 
                    ' from metadata can contain extension methods.
                    Return Me.GetTypeMembers() ' Ordered.
                End If
 
                Return ImmutableArray(Of NamedTypeSymbol).Empty
            End Get
        End Property
 
        Friend Function UnifyIfNoPiaLocalType(ByRef emittedTypeName As MetadataTypeName) As NamedTypeSymbol
            EnsureAllMembersLoaded()
            Dim typeDef As TypeDefinitionHandle = Nothing
 
            ' See if this is a NoPia local type, which we should unify.
            If _lazyNoPiaLocalTypes IsNot Nothing AndAlso
                _lazyNoPiaLocalTypes.TryGetValue(emittedTypeName.FullName, typeDef) Then
 
                Dim isNoPiaLocalType As Boolean
                Dim result = DirectCast(New MetadataDecoder(ContainingPEModule).GetTypeOfToken(typeDef, isNoPiaLocalType), NamedTypeSymbol)
                Debug.Assert(isNoPiaLocalType)
                Debug.Assert(result IsNot Nothing)
                Return result
            End If
 
            Return Nothing
        End Function
 
    End Class
 
End Namespace