/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PENamespaceSymbol.vb
329 строк
15 KB
Todd Grunke
Reduce allocations in PENamespaceSymbol.GetMembers() (#73794)
10 июн 2024, 21:48
Не верифицирован
10 июн 2024, 21:48
9f8660e
Код
Авторство
О чём код?
' 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