-
Notifications
You must be signed in to change notification settings - Fork 4.1k
/
Copy pathPENamespaceSymbol.vb
323 lines (260 loc) · 14.6 KB
/
PENamespaceSymbol.vb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
' 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)
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)
EnsureAllMembersLoaded()
Return m_lazyMembers.Flatten()
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