/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Declarations/DeclarationTable.vb
373 строки
18 KB
Jonathon Marolf
update headers
23 янв 2020, 04:01
Не верифицирован
23 янв 2020, 04:01
91571a3
Код
Авторство
О чём код?
' 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.Collections Imports Microsoft.CodeAnalysis.PooledObjects Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols ''' <summary> ''' A wrapper around RootSingleNamespaceDeclaration. The namespace declaration ''' is evaluated lazily to avoid evaluating the namespace and associated SyntaxTree ''' for embedded syntax trees before we can determine whether the syntax tree is needed. ''' </summary> Friend NotInheritable Class DeclarationTableEntry Public ReadOnly Root As Lazy(Of RootSingleNamespaceDeclaration) Public ReadOnly IsEmbedded As Boolean Public Sub New(root As Lazy(Of RootSingleNamespaceDeclaration), isEmbedded As Boolean) Me.Root = root Me.IsEmbedded = isEmbedded End Sub End Class ''' <summary> ''' A declaration table is a device which keeps track of type and namespace declarations from ''' parse trees. It is optimized for the case where there is one set of declarations that stays ''' constant, and a specific root namespace declaration corresponding to the currently edited ''' file which is being added and removed repeatedly. It maintains a cache of information for ''' "merging" the root declarations into one big summary declaration; this cache is efficiently ''' re-used provided that the pattern of adds and removes is as we expect. ''' </summary> Partial Friend Class DeclarationTable Public Shared ReadOnly Empty As DeclarationTable = New DeclarationTable( ImmutableSetWithInsertionOrder(Of DeclarationTableEntry).Empty, latestLazyRootDeclaration:=Nothing, cache:=Nothing) ' All our root declarations. We split these so we can separate out the unchanging 'older' ' declarations from the constantly changing 'latest' declaration. Private ReadOnly _allOlderRootDeclarations As ImmutableSetWithInsertionOrder(Of DeclarationTableEntry) Private ReadOnly _latestLazyRootDeclaration As DeclarationTableEntry ' The cache of computed values for the old declarations. Private ReadOnly _cache As Cache ' The lazily computed total merged declaration. Private _mergedRoot As MergedNamespaceDeclaration Private ReadOnly _typeNames As Lazy(Of ICollection(Of String)) Private ReadOnly _namespaceNames As Lazy(Of ICollection(Of String)) Private ReadOnly _referenceDirectives As Lazy(Of ICollection(Of ReferenceDirective)) Private _lazyAllRootDeclarations As ImmutableArray(Of RootSingleNamespaceDeclaration) Private Sub New(allOlderRootDeclarations As ImmutableSetWithInsertionOrder(Of DeclarationTableEntry), latestLazyRootDeclaration As DeclarationTableEntry, cache As Cache) Me._allOlderRootDeclarations = allOlderRootDeclarations Me._latestLazyRootDeclaration = latestLazyRootDeclaration Me._cache = If(cache, New Cache(Me)) Me._typeNames = New Lazy(Of ICollection(Of String))(AddressOf GetMergedTypeNames) Me._namespaceNames = New Lazy(Of ICollection(Of String))(AddressOf GetMergedNamespaceNames) Me._referenceDirectives = New Lazy(Of ICollection(Of ReferenceDirective))(AddressOf GetMergedReferenceDirectives) End Sub Public Function AddRootDeclaration(lazyRootDeclaration As DeclarationTableEntry) As DeclarationTable ' We can only re-use the cache if we don't already have a 'latest' item for the decl ' table. If _latestLazyRootDeclaration Is Nothing Then Return New DeclarationTable(_allOlderRootDeclarations, lazyRootDeclaration, Me._cache) Else ' we already had a 'latest' item. This means we're hearing about a change to a ' different tree. Realize the old latest item, add it to the 'older' collection ' and don't reuse the cache. Return New DeclarationTable(_allOlderRootDeclarations.Add(_latestLazyRootDeclaration), lazyRootDeclaration, cache:=Nothing) End If End Function Public Function Contains(rootDeclaration As DeclarationTableEntry) As Boolean Return rootDeclaration IsNot Nothing AndAlso (_allOlderRootDeclarations.Contains(rootDeclaration) OrElse _latestLazyRootDeclaration Is rootDeclaration) End Function Public Function RemoveRootDeclaration(lazyRootDeclaration As DeclarationTableEntry) As DeclarationTable ' We can only reuse the cache if we're removing the decl that was just added. If _latestLazyRootDeclaration Is lazyRootDeclaration Then Return New DeclarationTable(_allOlderRootDeclarations, latestLazyRootDeclaration:=Nothing, cache:=Me._cache) Else ' We're removing a different tree than the latest one added. We need to realize the ' passed in root and remove that from our 'older' list. We also can't reuse the ' cache. ' ' Note: we can keep around the 'latestLazyRootDeclaration'. There's no need to ' realize it if we don't have to. Return New DeclarationTable(_allOlderRootDeclarations.Remove(lazyRootDeclaration), _latestLazyRootDeclaration, cache:=Nothing) End If End Function Public Function AllRootNamespaces() As ImmutableArray(Of RootSingleNamespaceDeclaration) If _lazyAllRootDeclarations.IsDefault Then Dim builder = ArrayBuilder(Of RootSingleNamespaceDeclaration).GetInstance() GetOlderNamespaces(builder) Dim declOpt = GetLatestRootDeclarationIfAny(includeEmbedded:=True) If declOpt IsNot Nothing Then builder.Add(declOpt) End If ImmutableInterlocked.InterlockedInitialize(_lazyAllRootDeclarations, builder.ToImmutableAndFree()) End If Return _lazyAllRootDeclarations End Function Private Sub GetOlderNamespaces(builder As ArrayBuilder(Of RootSingleNamespaceDeclaration)) For Each olderRootDeclaration In _allOlderRootDeclarations.InInsertionOrder Dim declOpt = olderRootDeclaration.Root.Value If declOpt IsNot Nothing Then builder.Add(declOpt) End If Next End Sub Private Function MergeOlderNamespaces() As MergedNamespaceDeclaration Dim builder = ArrayBuilder(Of RootSingleNamespaceDeclaration).GetInstance() GetOlderNamespaces(builder) Dim result = MergedNamespaceDeclaration.Create(builder) builder.Free() Return result End Function Private Function SelectManyFromOlderDeclarationsNoEmbedded(Of T)(selector As Func(Of RootSingleNamespaceDeclaration, ImmutableArray(Of T))) As ImmutableArray(Of T) Return _allOlderRootDeclarations.InInsertionOrder.Where(Function(d) Not d.IsEmbedded AndAlso d.Root.Value IsNot Nothing).SelectMany(Function(d) selector(d.Root.Value)).AsImmutable() End Function ' The merged-tree-reuse story goes like this. We have a "forest" of old declarations, and ' possibly a lone tree of new declarations. We construct a merged declaration by merging ' together everything in the forest. This we can re-use from edit to edit, provided that ' nothing is added to or removed from the forest. We construct a merged declaration from the ' lone tree if there is one. (The lone tree might have nodes inside it that need merging, if ' there are two halves of one partial class.) Once we have two merged trees, we construct ' the full merged tree by merging them both together. So, diagrammatically, we have: ' ' MergedRoot ' / \ ' old merged root new merged root ' / | | | \ \ ' old singles forest new single tree Public Function GetMergedRoot(compilation As VisualBasicCompilation) As MergedNamespaceDeclaration Debug.Assert(compilation.Declarations Is Me) If _mergedRoot Is Nothing Then Interlocked.CompareExchange(_mergedRoot, CalculateMergedRoot(compilation), Nothing) End If Return _mergedRoot End Function ' Internal for unit tests only. Friend Function CalculateMergedRoot(compilation As VisualBasicCompilation) As MergedNamespaceDeclaration Dim oldRoot = Me._cache.MergedRoot.Value Dim latestRoot = GetLatestRootDeclarationIfAny(includeEmbedded:=True) If latestRoot Is Nothing Then Return oldRoot ElseIf oldRoot Is Nothing Then Return MergedNamespaceDeclaration.Create(latestRoot) Else Dim oldRootDeclarations = oldRoot.Declarations Dim builder = ArrayBuilder(Of SingleNamespaceDeclaration).GetInstance(oldRootDeclarations.Length + 1) builder.AddRange(oldRootDeclarations) builder.Add(_latestLazyRootDeclaration.Root.Value) ' Sort the root namespace declarations to match the order of SyntaxTrees. If compilation IsNot Nothing Then builder.Sort(New RootNamespaceLocationComparer(compilation)) End If Return MergedNamespaceDeclaration.Create(builder.ToImmutableAndFree()) End If End Function Private NotInheritable Class RootNamespaceLocationComparer Implements IComparer(Of SingleNamespaceDeclaration) Private ReadOnly _compilation As VisualBasicCompilation Friend Sub New(compilation As VisualBasicCompilation) _compilation = compilation End Sub <PerformanceSensitive( "https://github.com/dotnet/roslyn/issues/23582", Constraint:="Avoid " + NameOf(SingleNamespaceOrTypeDeclaration.Location) + " since it probably also has a costly allocation on this fast path (VB equivalent of issue found in C# code).")> Public Function Compare(x As SingleNamespaceDeclaration, y As SingleNamespaceDeclaration) As Integer Implements IComparer(Of SingleNamespaceDeclaration).Compare Return _compilation.CompareSourceLocations(x.SyntaxReference, y.SyntaxReference) End Function End Class Private Function GetMergedTypeNames() As ICollection(Of String) Dim cachedTypeNames = Me._cache.TypeNames.Value Dim latestRoot = GetLatestRootDeclarationIfAny(includeEmbedded:=True) If latestRoot Is Nothing Then Return cachedTypeNames Else Return UnionCollection(Of String).Create(cachedTypeNames, GetTypeNames(latestRoot)) End If End Function Private Function GetMergedNamespaceNames() As ICollection(Of String) Dim cachedNamespaceNames = Me._cache.NamespaceNames.Value Dim latestRoot = GetLatestRootDeclarationIfAny(includeEmbedded:=True) If latestRoot Is Nothing Then Return cachedNamespaceNames Else Return UnionCollection(Of String).Create(cachedNamespaceNames, GetNamespaceNames(latestRoot)) End If End Function Private Function GetMergedReferenceDirectives() As ICollection(Of ReferenceDirective) Dim cachedReferenceDirectives = _cache.ReferenceDirectives.Value Dim latestRoot = GetLatestRootDeclarationIfAny(includeEmbedded:=False) If latestRoot Is Nothing Then Return cachedReferenceDirectives Else Return UnionCollection(Of ReferenceDirective).Create(cachedReferenceDirectives, latestRoot.ReferenceDirectives) End If End Function Private Function GetLatestRootDeclarationIfAny(includeEmbedded As Boolean) As RootSingleNamespaceDeclaration Return If((_latestLazyRootDeclaration IsNot Nothing) AndAlso (includeEmbedded OrElse Not _latestLazyRootDeclaration.IsEmbedded), _latestLazyRootDeclaration.Root.Value, Nothing) End Function Private Shared ReadOnly s_isNamespacePredicate As Predicate(Of Declaration) = Function(d) d.Kind = DeclarationKind.Namespace Private Shared ReadOnly s_isTypePredicate As Predicate(Of Declaration) = Function(d) d.Kind <> DeclarationKind.Namespace Private Shared Function GetTypeNames(declaration As Declaration) As ICollection(Of String) Return GetNames(declaration, s_isTypePredicate) End Function Private Shared Function GetNamespaceNames(declaration As Declaration) As ICollection(Of String) Return GetNames(declaration, s_isNamespacePredicate) End Function Private Shared Function GetNames(declaration As Declaration, predicate As Predicate(Of Declaration)) As ICollection(Of String) Dim result = New IdentifierCollection Dim stack = New Stack(Of Declaration)() stack.Push(declaration) While stack.Count > 0 Dim current = stack.Pop() If current Is Nothing Then Continue While End If If predicate(current) Then result.AddIdentifier(current.Name) End If For Each child In current.Children stack.Push(child) Next End While Return result.AsCaseInsensitiveCollection() End Function Public ReadOnly Property TypeNames As ICollection(Of String) Get Return _typeNames.Value End Get End Property Public ReadOnly Property NamespaceNames As ICollection(Of String) Get Return _namespaceNames.Value End Get End Property Public ReadOnly Property ReferenceDirectives As ICollection(Of ReferenceDirective) Get Return _referenceDirectives.Value End Get End Property Public Shared Function ContainsName( mergedRoot As MergedNamespaceDeclaration, name As String, filter As SymbolFilter, cancellationToken As CancellationToken) As Boolean Return ContainsNameHelper( mergedRoot, Function(n) IdentifierComparison.Equals(n, name), filter, Function(t) t.MemberNames.Contains(name), cancellationToken) End Function Public Shared Function ContainsName( mergedRoot As MergedNamespaceDeclaration, predicate As Func(Of String, Boolean), filter As SymbolFilter, cancellationToken As CancellationToken) As Boolean Return ContainsNameHelper( mergedRoot, predicate, filter, Function(t) For Each name In t.MemberNames If predicate(name) Then Return True End If Next Return False End Function, cancellationToken) End Function Public Shared Function ContainsNameHelper( mergedRoot As MergedNamespaceDeclaration, predicate As Func(Of String, Boolean), filter As SymbolFilter, typePredicate As Func(Of SingleTypeDeclaration, Boolean), cancellationToken As CancellationToken) As Boolean Dim includeNamespace = (filter And SymbolFilter.Namespace) = SymbolFilter.Namespace Dim includeType = (filter And SymbolFilter.Type) = SymbolFilter.Type Dim includeMember = (filter And SymbolFilter.Member) = SymbolFilter.Member Dim stack = New Stack(Of MergedNamespaceOrTypeDeclaration)() stack.Push(mergedRoot) While stack.Count > 0 cancellationToken.ThrowIfCancellationRequested() Dim current = stack.Pop() If current Is Nothing Then Continue While End If If current.Kind = DeclarationKind.Namespace Then If includeNamespace AndAlso predicate(current.Name) Then Return True End If Else If includeType AndAlso predicate(current.Name) Then Return True End If If includeMember Then Dim mergedType = DirectCast(current, MergedTypeDeclaration) For Each childType In mergedType.Declarations If typePredicate(childType) Then Return True End If Next End If End If For Each child In current.Children Dim childNamespaceOrType = DirectCast(child, MergedNamespaceOrTypeDeclaration) If includeMember OrElse includeType OrElse childNamespaceOrType.Kind = DeclarationKind.Namespace Then stack.Push(childNamespaceOrType) End If Next End While Return False End Function End Class End Namespace