/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Symbols/EmbeddedSymbols/EmbeddedSymbolManager.SymbolsCollection.vb
391 строка
17 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.Concurrent Imports System.Collections.Generic Imports System.Runtime.InteropServices Imports System.Threading Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports TypeKind = Microsoft.CodeAnalysis.TypeKind Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Partial Friend NotInheritable Class EmbeddedSymbolManager Friend ReadOnly IsReferencedPredicate As Func(Of Symbol, Boolean) = Function(t) Not t.IsEmbedded OrElse Me.IsSymbolReferenced(t) Private ReadOnly _embedded As EmbeddedSymbolKind ''' <summary> Automatically embedded symbols (types, methods and fields) used in the current compilation </summary> Private ReadOnly _symbols As ConcurrentDictionary(Of Symbol, Boolean) ''' <summary> ''' Non-0 indicates that the collection of referenced symbols is sealed ''' and so no new symbols are supposed to be added. ''' </summary> Private _sealed As Integer = 0 ''' <summary> ''' True if StandardModuleAttribute was used in the current compilation ''' </summary> Private _standardModuleAttributeReferenced As Boolean = False Public Sub New(embedded As EmbeddedSymbolKind) ' Update assert if additional embedded kinds are expected. Debug.Assert((embedded And Not EmbeddedSymbolKind.All) = 0) _embedded = embedded If (embedded And EmbeddedSymbolKind.All) <> 0 Then ' If any bits are set, EmbeddedAttribute should be set. Debug.Assert((embedded And EmbeddedSymbolKind.EmbeddedAttribute) <> 0) _symbols = New ConcurrentDictionary(Of Symbol, Boolean)(ReferenceEqualityComparer.Instance) End If End Sub Public ReadOnly Property Embedded As EmbeddedSymbolKind Get Return _embedded End Get End Property ''' <summary> ''' Marks StandardModuleAttributeReference type as being references in the ''' current compilation. This method is to be used when a new type symbol for a ''' module is being created; we cannot pass the actual StandardModuleAttribute ''' type symbol because the symbol table is being constructed and calling ''' Compilation.GetWellKnownType(...) will cause infinite recursion. It does ''' not seem reasonable to special case this in symbol creation, so we just ''' mark StandardModuleAttribute attribute as referenced and then add ''' the actual symbol when MarkAllDeferredSymbols(...) is called. ''' </summary> Public Sub RegisterModuleDeclaration() If (_embedded And EmbeddedSymbolKind.VbCore) <> 0 Then _standardModuleAttributeReferenced = True End If End Sub #If DEBUG Then Private _markAllDeferredSymbolsAsReferencedIsCalled As Integer = ThreeState.Unknown #End If ''' <summary> ''' Mark all deferred types as referenced ''' </summary> Public Sub MarkAllDeferredSymbolsAsReferenced(compilation As VisualBasicCompilation) If Me._standardModuleAttributeReferenced Then MarkSymbolAsReferenced( compilation.GetWellKnownType( WellKnownType.Microsoft_VisualBasic_CompilerServices_StandardModuleAttribute)) End If #If DEBUG Then Interlocked.CompareExchange(_markAllDeferredSymbolsAsReferencedIsCalled, ThreeState.True, ThreeState.Unknown) #End If End Sub <Conditional("DEBUG")> Friend Sub AssertMarkAllDeferredSymbolsAsReferencedIsCalled() #If DEBUG Then Debug.Assert(Me._markAllDeferredSymbolsAsReferencedIsCalled = ThreeState.True) #End If End Sub ''' <summary> ''' Returns True if any embedded symbols are referenced. ''' ''' WARNING: the referenced symbols collection may not be sealed yet!!! ''' </summary> Public ReadOnly Property IsAnySymbolReferenced As Boolean Get Me.AssertMarkAllDeferredSymbolsAsReferencedIsCalled() Return (_symbols IsNot Nothing) AndAlso Not _symbols.IsEmpty End Get End Property ''' <summary> ''' Makes a snapshot of the current set of referenced symbols filtered by, ''' the set of symbols provided; may be called before the referenced symbol ''' collection is sealed. ''' </summary> Friend Sub GetCurrentReferencedSymbolsSnapshot(builder As ArrayBuilder(Of Symbol), filter As ConcurrentSet(Of Symbol)) Debug.Assert(builder IsNot Nothing) Debug.Assert(builder.Count = 0) Debug.Assert(filter IsNot Nothing) For Each pair In _symbols.ToArray() If Not filter.Contains(pair.Key) Then builder.Add(pair.Key) End If Next End Sub ''' <summary> ''' Checks if the embedded symbol provided is in the collection and adds it ''' into collection if not. ''' ''' See description of AddReferencedSymbolWithDependents for more details of how ''' it actually works. ''' </summary> Public Sub MarkSymbolAsReferenced(symbol As Symbol, allSymbols As ConcurrentSet(Of Symbol)) #If Not Debug Then ' In RELEASE don't add anything if the collection is sealed If _sealed <> 0 Then Return End If #End If Debug.Assert(symbol.IsDefinition) Debug.Assert(symbol.IsEmbedded) AddReferencedSymbolWithDependents(symbol, allSymbols) End Sub Public Sub MarkSymbolAsReferenced(symbol As Symbol) MarkSymbolAsReferenced(symbol, New ConcurrentSet(Of Symbol)(ReferenceEqualityComparer.Instance)) End Sub ''' <summary> ''' Returns True if the embedded symbol is known to be referenced in the current compilation. ''' </summary> Public Function IsSymbolReferenced(symbol As Symbol) As Boolean Debug.Assert(symbol.IsEmbedded) Me.AssertMarkAllDeferredSymbolsAsReferencedIsCalled() Return _symbols.TryGetValue(symbol, Nothing) End Function ''' <summary> ''' Seals the collection of referenced symbols, all *new* symbols passed ''' to SpawnSymbolCollection(...) will cause assert and be ignored. ''' </summary> Public Sub SealCollection() Interlocked.CompareExchange(_sealed, 1, 0) End Sub #Region "Add referenced symbol implementation" ''' <summary> ''' Checks if the embedded symbol provided is present in the 'allSymbols' and if not ''' adds it into 'allSymbols' as well as to the collection of referenced symbols ''' managed by this manager. Also adds all the 'dependent' symbols, i.e. symbols ''' which must also be marked as referenced if 'symbol' is referenced. ''' ''' NOTE that when a new embedded symbol is being added to the collection of referenced ''' symbols it should be added along with all the 'dependent' symbols. For example, if ''' we add a method symbol (T1.M1) we should ensure the containing type symbol (T1) is ''' added too, as well as its constructor (T1..ctor) and maybe attribute(s) (Attr1) set ''' on T1 and their constructors/fields (Attr1..ctor), etc... ''' ''' All dependent symbols must be added in the current thread not depending on ''' the other concurrent threads and avoiding possible race. Thus, let's suppose we have ''' the following dependencies: ''' ''' T1.M1 -> { T1, T1..ctor, Attr1, Attr1..ctor, ... } ''' ''' we cannot just check if T1.M1 exists in the collection of referenced symbols and not ''' add dependent symbols if it does; the reason is that T1.M1 may be added by a concurrent ''' thread, but its dependencies may not be added by that thread yet. So we need to ''' calculate all dependencies and try add all the symbols together. ''' ''' On the other hand it should be avoided that the method *always* goes through all ''' the dependencies for each symbol even though it may be definitely known that the symbol ''' is added in one of the previous operations by *the same thread*. To serve this purpose ''' the method uses 'allSymbols' collection to actually check whether or not the symbol ''' is added to the collection. This makes possible to reuse the same collection in several ''' consequent calls to AddReferencedSymbolWithDependents from the same thread; for example ''' in case one thread consequently adds lots of symbols, the thread may use the same ''' 'allSymbols' instance for efficient symbol filtering. ''' </summary> Private Sub AddReferencedSymbolWithDependents(symbol As Symbol, allSymbols As ConcurrentSet(Of Symbol)) If Not symbol.IsEmbedded Then Return End If Debug.Assert(symbol.IsDefinition) If allSymbols.Contains(symbol) Then Return ' was added in this thread before End If Select Case symbol.Kind Case SymbolKind.Field ' add the symbol itself AddReferencedSymbolRaw(symbol, allSymbols) ' add the containing type AddReferencedSymbolWithDependents(symbol.ContainingType, allSymbols) Case SymbolKind.Method ' add the symbol itself AddReferencedSymbolRaw(symbol, allSymbols) ' add the containing type AddReferencedSymbolWithDependents(symbol.ContainingType, allSymbols) ' if the method is an accessor Dim methKind As MethodKind = DirectCast(symbol, MethodSymbol).MethodKind Select Case methKind Case MethodKind.PropertyGet, MethodKind.PropertySet ' add associated property, note that adding any accessor will cause ' adding the property as well as the other accessor if any AddReferencedSymbolWithDependents(DirectCast(symbol, MethodSymbol).AssociatedSymbol, allSymbols) Case MethodKind.Ordinary, MethodKind.Constructor, MethodKind.SharedConstructor ' OK Case Else Throw ExceptionUtilities.UnexpectedValue(methKind) End Select Case SymbolKind.Property ' add the symbol itself AddReferencedSymbolRaw(symbol, allSymbols) ' add the containing type AddReferencedSymbolWithDependents(symbol.ContainingType, allSymbols) ' add accessors Dim [property] = DirectCast(symbol, PropertySymbol) If [property].GetMethod IsNot Nothing Then AddReferencedSymbolWithDependents([property].GetMethod, allSymbols) End If If [property].SetMethod IsNot Nothing Then AddReferencedSymbolWithDependents([property].SetMethod, allSymbols) End If Case SymbolKind.NamedType ValidateType(DirectCast(symbol, NamedTypeSymbol)) ' add the symbol itself AddReferencedSymbolRaw(symbol, allSymbols) ' add SOME type members For Each member In DirectCast(symbol, NamedTypeSymbol).GetMembers() Select Case member.Kind Case SymbolKind.Field ' Always add non-const fields If Not DirectCast(member, FieldSymbol).IsConst Then AddReferencedSymbolRaw(member, allSymbols) ' fields of embedded types are not supported Debug.Assert(Not DirectCast(member, FieldSymbol).Type.IsEmbedded) End If Case SymbolKind.Method Select Case DirectCast(member, MethodSymbol).MethodKind Case MethodKind.SharedConstructor, MethodKind.Constructor ' Add constructors AddReferencedSymbolRaw(member, allSymbols) End Select ' Don't add regular methods, all of them should be added on-demand ' All other method kinds should not get here, it is asserted in ValidateType(...) End Select Next If symbol.ContainingType IsNot Nothing Then AddReferencedSymbolWithDependents(symbol.ContainingType, allSymbols) End If End Select End Sub Private Sub AddReferencedSymbolRaw(symbol As Symbol, allSymbols As ConcurrentSet(Of Symbol)) Debug.Assert(symbol.Kind = SymbolKind.NamedType OrElse symbol.Kind = SymbolKind.Property OrElse symbol.Kind = SymbolKind.Method OrElse symbol.Kind = SymbolKind.Field) If allSymbols.Add(symbol) Then If _sealed <> 0 Then ' Collection is sealed Debug.Assert(_symbols.ContainsKey(symbol)) Else _symbols.TryAdd(symbol, True) ' NOTE: there is still a chance that a new element is added to a sealed collection End If ' add symbol's attributes For Each attribute In symbol.GetAttributes() AddReferencedSymbolWithDependents(attribute.AttributeClass, allSymbols) Next End If End Sub #End Region <Conditional("DEBUG")> Private Shared Sub ValidateType(type As NamedTypeSymbol) Debug.Assert(type.TypeKind = TypeKind.Module OrElse type.TypeKind = TypeKind.Class AndAlso type.IsNotInheritable) For Each member In type.GetMembers() Select Case member.Kind Case SymbolKind.Field ValidateField(DirectCast(member, FieldSymbol)) Case SymbolKind.Method ValidateMethod(DirectCast(member, MethodSymbol)) Case SymbolKind.NamedType ' Nested types are OK Case SymbolKind.Property ' Properties are OK if the accessors are OK, and accessors will be ' checked separately since those will also appear in GetMembers(). Case Else ' No other symbol kinds are allowed Throw ExceptionUtilities.UnexpectedValue(member.Kind) End Select Next End Sub <Conditional("DEBUG")> Private Shared Sub ValidateField(field As FieldSymbol) ' Fields are OK (initializers are checked in method compiler) Dim type = field.Type Debug.Assert(Not type.IsEmbedded OrElse type.IsTypeParameter) End Sub <Conditional("DEBUG")> Friend Shared Sub ValidateMethod(method As MethodSymbol) ' Constructors, regular methods, and property accessors are OK Dim kind = method.MethodKind Debug.Assert(kind = MethodKind.Constructor OrElse kind = MethodKind.SharedConstructor OrElse kind = MethodKind.Ordinary OrElse kind = MethodKind.PropertyGet OrElse kind = MethodKind.PropertySet) Debug.Assert(Not method.IsOverridable) Debug.Assert(method.ExplicitInterfaceImplementations.IsEmpty) Debug.Assert(Not method.ReturnType.IsEmbedded OrElse method.ReturnType.IsTypeParameter) Debug.Assert(method.GetReturnTypeAttributes().IsEmpty) For Each parameter In method.Parameters Debug.Assert(Not parameter.Type.IsEmbedded OrElse parameter.Type.IsTypeParameter) Debug.Assert(parameter.GetAttributes().IsEmpty) Next End Sub End Class End Namespace