File: Symbols\PEOrSourceOrMergedNamespaceSymbol.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.Immutable
Imports System.Threading
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols
    ''' <summary>
    ''' Represents a namespace.
    ''' </summary>
    Friend MustInherit Class PEOrSourceOrMergedNamespaceSymbol
        Inherits NamespaceSymbol
 
        ''' <summary>
        ''' For a given namespace in context of a particular Compilation all binders use 
        ''' either a compilation merged namespace symbol, or a module level namespace symbol 
        ''' (PE, Source or Retargeting). In order to speed-up lookup of extension methods performed 
        ''' by a binder, we build and cache a map of all extension methods declared within the namespace 
        ''' grouped by name (case-insensitively). 
        ''' 
        ''' If binder uses compilation merged namespace symbol, the map is built across all underlying 
        ''' module level namespace symbols, separate maps for underlying namespace symbols are not built.
        ''' 
        ''' If binder uses Retargeting module level namespace symbol, we build the map for the underlying 
        ''' namespace symbol instead and push all requests through the underlying namespace.
        ''' 
        ''' The map actually stores ImmutableArray(Of MethodSymbol), but we are using ImmutableArray(Of Symbol)
        ''' in order to be able to pass the map to a more general API.
        ''' </summary>
        Private _lazyExtensionMethodsMap As Dictionary(Of String, ImmutableArray(Of Symbol))
 
        ' counter of extension method queries against this namespace until we decide to build complete map
        Private _extQueryCnt As Integer
 
        Private Shared ReadOnly s_emptyDictionary As New Dictionary(Of String, ImmutableArray(Of Symbol))()
 
        Private _lazyDeclaredAccessibilityOfMostAccessibleDescendantType As Byte = CByte(Accessibility.Private) ' Not calculated yet.
 
        Friend ReadOnly Property RawLazyDeclaredAccessibilityOfMostAccessibleDescendantType As Accessibility
            Get
                Return CType(_lazyDeclaredAccessibilityOfMostAccessibleDescendantType, Accessibility)
            End Get
        End Property
 
        Friend MustOverride Overrides ReadOnly Property EmbeddedSymbolKind As EmbeddedSymbolKind
 
        ''' <summary>
        ''' Returns declared accessibility of most accessible type within this namespace or within a containing namespace recursively.
        ''' Valid return values:
        '''     Friend,
        '''     Public,
        '''     NotApplicable - if there are no types.
        ''' </summary>
        Friend NotOverridable Overrides ReadOnly Property DeclaredAccessibilityOfMostAccessibleDescendantType As Accessibility
            Get
                If _lazyDeclaredAccessibilityOfMostAccessibleDescendantType = Accessibility.Private Then
                    _lazyDeclaredAccessibilityOfMostAccessibleDescendantType = CByte(GetDeclaredAccessibilityOfMostAccessibleDescendantType())
 
                    ' Bubble up public accessibility
                    If _lazyDeclaredAccessibilityOfMostAccessibleDescendantType = Accessibility.Public Then
                        Dim parent = TryCast(Me.ContainingSymbol, PEOrSourceOrMergedNamespaceSymbol)
 
                        While parent IsNot Nothing AndAlso
                              parent._lazyDeclaredAccessibilityOfMostAccessibleDescendantType = Accessibility.Private
 
                            parent._lazyDeclaredAccessibilityOfMostAccessibleDescendantType = CByte(Accessibility.Public)
                            parent = TryCast(parent.ContainingSymbol, PEOrSourceOrMergedNamespaceSymbol)
                        End While
                    End If
                End If
 
                Return CType(_lazyDeclaredAccessibilityOfMostAccessibleDescendantType, Accessibility)
            End Get
        End Property
 
        ''' <summary>
        ''' This is an entry point for the Binder to collect extension methods with the given name 
        ''' declared within this (compilation merged or module level) namespace, so that methods 
        ''' from the same type are grouped together. 
        ''' 
        ''' A cached map of extension methods is used to optimize the lookup.
        ''' </summary>
        Friend Overrides Sub AppendProbableExtensionMethods(name As String, methods As ArrayBuilder(Of MethodSymbol))
            Dim match As ImmutableArray(Of Symbol) = Nothing
 
            If _lazyExtensionMethodsMap Is Nothing Then
                ' Do not force collection of all extension methods as that might be expensive
                ' unless we see a lot of traffic here
                Dim cnt = Interlocked.Increment(Me._extQueryCnt)
 
                ' 40 is a rough threshold when we consider current namespace to be popular enough to build a complete
                ' cache of extension methods. Bigger number would favor dynamic scenarios like typing (vs. static compiling)
                ' by delaying collection of all extension methods which may be unnecessary if user continues typing.
                ' 
                ' when I tested some typing scenarios in VB compiler, it seems that when this 
                ' number in 30-50 range the time spent in GetExtensionMethods and EnsureExtensionMethodsAreCollected
                ' are on the same order. Overall perf is not very sensitive to the threshold. 
                '
                ' "=" because we want only one thread to do collecting.
                If cnt = 40 Then
                    EnsureExtensionMethodsAreCollected()
                Else
                    ' get just the extension methods of the given name. 
                    GetExtensionMethods(methods, name)
                    Return
                End If
            End If
 
            If _lazyExtensionMethodsMap.TryGetValue(name, match) Then
                methods.AddRange(match.As(Of MethodSymbol))
            End If
        End Sub
 
        ''' <summary>
        ''' Add names of viable extension methods declared in this (compilation merged or module level) 
        ''' namespace to nameSet parameter.
        ''' 
        ''' The 'appendThrough' parameter allows RetargetingNamespaceSymbol to delegate majority of the work 
        ''' to the underlying namespace symbol, but still perform viability check on RetargetingMethodSymbol.
        ''' 
        ''' A cached map of extension methods is used to optimize the operation.
        ''' </summary>
        Friend Overrides Sub AddExtensionMethodLookupSymbolsInfo(nameSet As LookupSymbolsInfo,
                                                                  options As LookupOptions,
                                                                  originalBinder As Binder,
                                                                  appendThrough As NamespaceSymbol)
            EnsureExtensionMethodsAreCollected()
            appendThrough.AddExtensionMethodLookupSymbolsInfo(nameSet, options, originalBinder, _lazyExtensionMethodsMap)
        End Sub
 
        ''' <summary>
        ''' Build and cache a map of probable extension methods for this namespace.
        ''' </summary>
        Private Sub EnsureExtensionMethodsAreCollected()
            If _lazyExtensionMethodsMap Is Nothing Then
                Dim map As New Dictionary(Of String, ArrayBuilder(Of MethodSymbol))(CaseInsensitiveComparison.Comparer)
                BuildExtensionMethodsMap(map)
 
                If map.Count = 0 Then
                    _lazyExtensionMethodsMap = s_emptyDictionary
                Else
                    Dim extensionMethods As New Dictionary(Of String, ImmutableArray(Of Symbol))(map.Count, CaseInsensitiveComparison.Comparer)
 
                    For Each pair As KeyValuePair(Of String, ArrayBuilder(Of MethodSymbol)) In map
                        extensionMethods.Add(pair.Key, StaticCast(Of Symbol).From(pair.Value.ToImmutableAndFree()))
                    Next
 
                    _lazyExtensionMethodsMap = extensionMethods
                End If
            End If
        End Sub
 
    End Class
 
End Namespace