/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Symbols/ReferenceManager.vb
1 028 строк
57 KB
Todd Grunke
Change AssemblyData.AvailableSymbols to ImmutableArray instead of IEnumerable (#70087)
24 сен 2023, 04:46
Не верифицирован
24 сен 2023, 04:46
ca528c9
Код
Авторство
О чём код?
' 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.Runtime.InteropServices Imports Microsoft.CodeAnalysis.Collections Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Emit Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Retargeting Imports MetadataOrDiagnostic = System.Object Namespace Microsoft.CodeAnalysis.VisualBasic Partial Public NotInheritable Class VisualBasicCompilation ''' <summary> ''' ReferenceManager encapsulates functionality to create an underlying SourceAssemblySymbol ''' (with underlying ModuleSymbols) for Compilation and AssemblySymbols for referenced assemblies ''' (with underlying ModuleSymbols) all properly linked together based on reference resolution ''' between them. ''' ''' ReferenceManager is also responsible for reuse of metadata readers for imported modules and ''' assemblies as well as existing AssemblySymbols for referenced assemblies. In order to do that, ''' it maintains global cache for metadata readers and AssemblySymbols associated with them. ''' The cache uses WeakReferences to refer to the metadata readers and AssemblySymbols to allow ''' memory and resources being reclaimed once they are no longer used. The tricky part about reusing ''' existing AssemblySymbols is to find a set of AssemblySymbols that are created for the referenced ''' assemblies, which (the AssemblySymbols from the set) are linked in a way, consistent with the ''' reference resolution between the referenced assemblies. ''' ''' When existing Compilation is used as a metadata reference, there are scenarios when its underlying ''' SourceAssemblySymbol cannot be used to provide symbols in context of the new Compilation. Consider ''' classic multi-targeting scenario: compilation C1 references v1 of Lib.dll and compilation C2 ''' references C1 and v2 of Lib.dll. In this case, SourceAssemblySymbol for C1 is linked to AssemblySymbol ''' for v1 of Lib.dll. However, given the set of references for C2, the same reference for C1 should be ''' resolved against v2 of Lib.dll. In other words, in context of C2, all types from v1 of Lib.dll ''' leaking through C1 (through method signatures, etc.) must be retargeted to the types from v2 of Lib.dll. ''' In this case, ReferenceManager creates a special RetargetingAssemblySymbol for C1, which is responsible ''' for the type retargeting. The RetargetingAssemblySymbols could also be reused for different ''' Compilations, ReferenceManager maintains a cache of RetargetingAssemblySymbols (WeakReferences) for each ''' Compilation. ''' ''' The only public entry point of this class is CreateSourceAssembly() method. ''' </summary> Friend NotInheritable Class ReferenceManager Inherits CommonReferenceManager(Of VisualBasicCompilation, AssemblySymbol) Public Sub New(simpleAssemblyName As String, identityComparer As AssemblyIdentityComparer, observedMetadata As Dictionary(Of MetadataReference, MetadataOrDiagnostic)) MyBase.New(simpleAssemblyName, identityComparer, observedMetadata) End Sub Protected Overrides Sub GetActualBoundReferencesUsedBy(assemblySymbol As AssemblySymbol, referencedAssemblySymbols As List(Of AssemblySymbol)) Debug.Assert(referencedAssemblySymbols.IsEmpty()) For Each [module] In assemblySymbol.Modules referencedAssemblySymbols.AddRange([module].GetReferencedAssemblySymbols()) Next For i As Integer = 0 To referencedAssemblySymbols.Count - 1 Step 1 If referencedAssemblySymbols(i).IsMissing Then referencedAssemblySymbols(i) = Nothing ' Do not expose missing assembly symbols to ReferenceManager.Binder End If Next End Sub Protected Overrides Function GetNoPiaResolutionAssemblies(candidateAssembly As AssemblySymbol) As ImmutableArray(Of AssemblySymbol) If TypeOf candidateAssembly Is SourceAssemblySymbol Then ' This is an optimization, if candidateAssembly links something or explicitly declares local type, ' common reference binder shouldn't reuse this symbol because candidateAssembly won't be in the ' set returned by GetNoPiaResolutionAssemblies(). This also makes things clearer. Return ImmutableArray(Of AssemblySymbol).Empty End If Return candidateAssembly.GetNoPiaResolutionAssemblies() End Function Protected Overrides Function IsLinked(candidateAssembly As AssemblySymbol) As Boolean Return candidateAssembly.IsLinked End Function Protected Overrides Function GetCorLibrary(candidateAssembly As AssemblySymbol) As AssemblySymbol Dim corLibrary As AssemblySymbol = candidateAssembly.CorLibrary ' Do not expose missing assembly symbols to ReferenceManager.Binder Return If(corLibrary.IsMissing, Nothing, corLibrary) End Function Protected Overrides ReadOnly Property MessageProvider As CommonMessageProvider Get Return VisualBasic.MessageProvider.Instance End Get End Property Protected Overrides Function CreateAssemblyDataForFile(assembly As PEAssembly, cachedSymbols As WeakList(Of IAssemblySymbolInternal), documentationProvider As DocumentationProvider, sourceAssemblySimpleName As String, importOptions As MetadataImportOptions, embedInteropTypes As Boolean) As AssemblyData Return New AssemblyDataForFile(assembly, cachedSymbols, embedInteropTypes, documentationProvider, sourceAssemblySimpleName, importOptions) End Function Protected Overrides Function CreateAssemblyDataForCompilation(compilationReference As CompilationReference) As AssemblyData Dim vbReference = TryCast(compilationReference, VisualBasicCompilationReference) If vbReference Is Nothing Then Throw New NotSupportedException(String.Format(VBResources.CantReferenceCompilationFromTypes, compilationReference.GetType(), "Visual Basic")) End If Dim result As New AssemblyDataForCompilation(vbReference.Compilation, vbReference.Properties.EmbedInteropTypes) Debug.Assert(vbReference.Compilation._lazyAssemblySymbol IsNot Nothing) Return result End Function Protected Overrides Function CheckPropertiesConsistency(primaryReference As MetadataReference, duplicateReference As MetadataReference, diagnostics As DiagnosticBag) As Boolean Return True End Function ''' <summary> ''' VB allows two weak assembly references of the same simple name be passed to a compilation ''' as long as their versions are different. It ignores culture. ''' </summary> Protected Overrides Function WeakIdentityPropertiesEquivalent(identity1 As AssemblyIdentity, identity2 As AssemblyIdentity) As Boolean Return identity1.Version = identity2.Version End Function Public Sub CreateSourceAssemblyForCompilation(compilation As VisualBasicCompilation) ' We are reading the Reference Manager state outside of a lock by accessing ' IsBound and HasCircularReference properties. ' Once isBound flag is flipped the state of the manager is available and doesn't change. ' ' If two threads are building SourceAssemblySymbol and the first just updated ' set isBound flag to 1 but not yet set lazySourceAssemblySymbol, ' the second thread may end up reusing the Reference Manager data the first thread calculated. ' That's ok since ' 1) the second thread would produce the same data, ' 2) all results calculated by the second thread will be thrown away since the first thread ' already acquired SymbolCacheAndReferenceManagerStateGuard that is needed to publish the data. ' Given compilation is the first compilation that shares this manager and its symbols are requested. ' Perform full reference resolution and binding. If Not IsBound AndAlso CreateAndSetSourceAssemblyFullBind(compilation) Then ' we have successfully bound the references for the compilation ElseIf Not HasCircularReference Then ' Another compilation that shares the manager with the given compilation ' already bound its references and produced tables that we can use to construct ' source assembly symbol faster. CreateAndSetSourceAssemblyReuseData(compilation) Else ' We encountered a circular reference while binding the previous compilation. ' This compilation can't share bound references with other compilations. Create a new manager. ' NOTE: The CreateSourceAssemblyFullBind is going to replace compilation's reference manager with newManager. Dim newManager = New ReferenceManager(Me.SimpleAssemblyName, Me.IdentityComparer, Me.ObservedMetadata) Dim successful = newManager.CreateAndSetSourceAssemblyFullBind(compilation) ' The new manager isn't shared with any other compilation so there is no other ' thread but the current one could have initialized it. Debug.Assert(successful) newManager.AssertBound() End If AssertBound() Debug.Assert(compilation._lazyAssemblySymbol IsNot Nothing) End Sub ''' <summary> ''' Creates a <see cref="PEAssemblySymbol"/> from specified metadata. ''' </summary> ''' <remarks> ''' Used by EnC to create symbols for emit baseline. The PE symbols are used by <see cref="VisualBasicSymbolMatcher"/>. ''' ''' The assembly references listed in the metadata AssemblyRef table are matched to the resolved references ''' stored on this <see cref="ReferenceManager"/>. We assume that the dependencies of the baseline metadata are ''' the same as the dependencies of the current compilation. This is not exactly true when the dependencies use ''' time-based versioning pattern, e.g. AssemblyVersion("1.0.*"). In that case we assume only the version ''' changed And nothing else. ''' ''' Each AssemblyRef is matched against the assembly identities using an exact equality comparison modulo version. ''' AssemblyRef with lower version in metadata is matched to a PE assembly symbol with the higher version ''' (provided that the assembly name, culture, PKT And flags are the same) if there is no symbol with the exactly matching version. ''' If there are multiple symbols with higher versions selects the one with the minimal version among them. ''' ''' Matching to a higher version is necessary to support EnC for projects whose P2P dependencies use time-based versioning pattern. ''' The versions of the dependent projects seen from the IDE will be higher than ''' the one written in the metadata at the time their respective baselines are built. ''' ''' No other unification or further resolution is performed. ''' </remarks> ''' <param name="metadata"></param> ''' <param name="importOptions"></param> ''' <param name="assemblyReferenceIdentityMap"> ''' A map of the PE assembly symbol identities to the identities of the original metadata AssemblyRefs. ''' This map will be used in emit when serializing AssemblyRef table of the delta. For the delta to be compatible with ''' the original metadata we need to map the identities of the PE assembly symbols back to the original AssemblyRefs (if different). ''' In other words, we pretend that the versions of the dependencies haven't changed. ''' </param> Friend Function CreatePEAssemblyForAssemblyMetadata(metadata As AssemblyMetadata, importOptions As MetadataImportOptions, <Out> ByRef assemblyReferenceIdentityMap As ImmutableDictionary(Of AssemblyIdentity, AssemblyIdentity)) As PEAssemblySymbol AssertBound() ' If the compilation has a reference from metadata to source assembly we can't share the referenced PE symbols. Debug.Assert(Not HasCircularReference) Dim referencedAssembliesByIdentity = New AssemblyIdentityMap(Of AssemblySymbol)() For Each symbol In Me.ReferencedAssemblies referencedAssembliesByIdentity.Add(symbol.Identity, symbol) Next Dim assembly = metadata.GetAssembly Dim peReferences = assembly.AssemblyReferences.SelectAsArray(AddressOf MapAssemblyIdentityToResolvedSymbol, referencedAssembliesByIdentity) assemblyReferenceIdentityMap = GetAssemblyReferenceIdentityBaselineMap(peReferences, assembly.AssemblyReferences) Dim assemblySymbol = New PEAssemblySymbol(assembly, DocumentationProvider.Default, isLinked:=False, importOptions:=importOptions) Dim unifiedAssemblies = Me.UnifiedAssemblies.WhereAsArray( Function(unified, refAsmByIdentity) refAsmByIdentity.Contains(unified.OriginalReference, allowHigherVersion:=False), referencedAssembliesByIdentity) InitializeAssemblyReuseData(assemblySymbol, peReferences, unifiedAssemblies) If assembly.ContainsNoPiaLocalTypes() Then assemblySymbol.SetNoPiaResolutionAssemblies(Me.ReferencedAssemblies) End If Return assemblySymbol End Function Private Shared Function MapAssemblyIdentityToResolvedSymbol(identity As AssemblyIdentity, map As AssemblyIdentityMap(Of AssemblySymbol)) As AssemblySymbol Dim symbol As AssemblySymbol = Nothing If map.TryGetValue(identity, symbol, AddressOf CompareVersionPartsSpecifiedInSource) Then Return symbol End If If map.TryGetValue(identity, symbol, Function(v1, v2, s) True) Then ' TODO: https://github.com/dotnet/roslyn/issues/9004 Throw New NotSupportedException(String.Format(CodeAnalysisResources.ChangingVersionOfAssemblyReferenceIsNotAllowedDuringDebugging, identity, symbol.Identity.Version)) End If Return New MissingAssemblySymbol(identity) End Function Private Sub CreateAndSetSourceAssemblyReuseData(compilation As VisualBasicCompilation) AssertBound() ' If the compilation has a reference from metadata to source assembly we can't share the referenced PE symbols. Debug.Assert(Not HasCircularReference) Dim moduleName As String = compilation.MakeSourceModuleName() Dim assemblySymbol = New SourceAssemblySymbol(compilation, Me.SimpleAssemblyName, moduleName, Me.ReferencedModules) InitializeAssemblyReuseData(assemblySymbol, Me.ReferencedAssemblies, Me.UnifiedAssemblies) If compilation._lazyAssemblySymbol Is Nothing Then SyncLock SymbolCacheAndReferenceManagerStateGuard If compilation._lazyAssemblySymbol Is Nothing Then compilation._lazyAssemblySymbol = assemblySymbol Debug.Assert(compilation._referenceManager Is Me) End If End SyncLock End If End Sub Private Sub InitializeAssemblyReuseData(assemblySymbol As AssemblySymbol, referencedAssemblies As ImmutableArray(Of AssemblySymbol), unifiedAssemblies As ImmutableArray(Of UnifiedAssembly(Of AssemblySymbol))) AssertBound() assemblySymbol.SetCorLibrary(If(Me.CorLibraryOpt, assemblySymbol)) Dim sourceModuleReferences = New ModuleReferences(Of AssemblySymbol)(referencedAssemblies.SelectAsArray(Function(a) a.Identity), referencedAssemblies, unifiedAssemblies) assemblySymbol.Modules(0).SetReferences(sourceModuleReferences) Dim assemblyModules = assemblySymbol.Modules Dim referencedModulesReferences = Me.ReferencedModulesReferences Debug.Assert(assemblyModules.Length = referencedModulesReferences.Length + 1) For i = 1 To assemblyModules.Length - 1 assemblyModules(i).SetReferences(referencedModulesReferences(i - 1)) Next End Sub ' Returns false if another compilation sharing this manager finished binding earlier and we should reuse its results. Friend Function CreateAndSetSourceAssemblyFullBind(compilation As VisualBasicCompilation) As Boolean Dim resolutionDiagnostics = DiagnosticBag.GetInstance() Dim supersedeLowerVersions = compilation.Options.ReferencesSupersedeLowerVersions Dim assemblyReferencesBySimpleName = PooledDictionary(Of String, List(Of ReferencedAssemblyIdentity)).GetInstance() Try Dim boundReferenceDirectiveMap As IDictionary(Of ValueTuple(Of String, String), MetadataReference) = Nothing Dim boundReferenceDirectives As ImmutableArray(Of MetadataReference) = Nothing Dim referencedAssemblies As ImmutableArray(Of AssemblyData) = Nothing Dim modules As ImmutableArray(Of PEModule) = Nothing ' To make sure the modules are not collected ahead of time. Dim explicitReferences As ImmutableArray(Of MetadataReference) = Nothing Dim referenceMap As ImmutableArray(Of ResolvedReference) = ResolveMetadataReferences( compilation, assemblyReferencesBySimpleName, explicitReferences, boundReferenceDirectiveMap, boundReferenceDirectives, referencedAssemblies, modules, resolutionDiagnostics) Dim assemblyBeingBuiltData As New AssemblyDataForAssemblyBeingBuilt(New AssemblyIdentity(name:=SimpleAssemblyName, noThrow:=True), referencedAssemblies, modules) Dim explicitAssemblyData = referencedAssemblies.Insert(0, assemblyBeingBuiltData) ' Let's bind all the references and resolve missing one (if resolver is available) Dim hasCircularReference As Boolean Dim corLibraryIndex As Integer Dim implicitlyResolvedReferences As ImmutableArray(Of MetadataReference) = Nothing Dim implicitlyResolvedReferenceMap As ImmutableArray(Of ResolvedReference) = Nothing Dim allAssemblyData As ImmutableArray(Of AssemblyData) = Nothing ' Avoid resolving previously resolved missing references. If we call to the resolver again we would create new assembly symbols for them, ' which would not match the previously created ones. As a result we would get duplicate PE types And conversion errors. Dim implicitReferenceResolutions = If(compilation.ScriptCompilationInfo?.PreviousScriptCompilation?.GetBoundReferenceManager().ImplicitReferenceResolutions, ImmutableDictionary(Of AssemblyIdentity, PortableExecutableReference).Empty) Dim bindingResult() As BoundInputAssembly = Bind(explicitAssemblyData, modules, explicitReferences, referenceMap, compilation.Options.MetadataReferenceResolver, compilation.Options.MetadataImportOptions, supersedeLowerVersions, assemblyReferencesBySimpleName, allAssemblyData, implicitlyResolvedReferences, implicitlyResolvedReferenceMap, implicitReferenceResolutions, resolutionDiagnostics, hasCircularReference, corLibraryIndex) Debug.Assert(bindingResult.Length = allAssemblyData.Length) Dim references = explicitReferences.AddRange(implicitlyResolvedReferences) referenceMap = referenceMap.AddRange(implicitlyResolvedReferenceMap) Dim referencedAssembliesMap As Dictionary(Of MetadataReference, Integer) = Nothing Dim referencedModulesMap As Dictionary(Of MetadataReference, Integer) = Nothing Dim aliasesOfReferencedAssemblies As ImmutableArray(Of ImmutableArray(Of String)) = Nothing Dim mergedAssemblyReferencesMapOpt As Dictionary(Of MetadataReference, ImmutableArray(Of MetadataReference)) = Nothing BuildReferencedAssembliesAndModulesMaps( bindingResult, references, referenceMap, modules.Length, referencedAssemblies.Length, assemblyReferencesBySimpleName, supersedeLowerVersions, referencedAssembliesMap, referencedModulesMap, aliasesOfReferencedAssemblies, mergedAssemblyReferencesMapOpt) ' Create AssemblySymbols for assemblies that can't use any existing symbols. Dim newSymbols As New List(Of Integer) For i As Integer = 1 To bindingResult.Length - 1 Step 1 If bindingResult(i).AssemblySymbol Is Nothing Then ' symbol hasn't been found in the cache, create a new one bindingResult(i).AssemblySymbol = DirectCast(allAssemblyData(i), AssemblyDataForMetadataOrCompilation).CreateAssemblySymbol() newSymbols.Add(i) End If Debug.Assert(allAssemblyData(i).IsLinked = bindingResult(i).AssemblySymbol.IsLinked) Next Dim assemblySymbol = New SourceAssemblySymbol(compilation, SimpleAssemblyName, compilation.MakeSourceModuleName(), modules) Dim corLibrary As AssemblySymbol If corLibraryIndex = 0 Then corLibrary = assemblySymbol ElseIf corLibraryIndex > 0 Then corLibrary = bindingResult(corLibraryIndex).AssemblySymbol Else corLibrary = MissingCorLibrarySymbol.Instance End If assemblySymbol.SetCorLibrary(corLibrary) ' Setup bound references for newly created AssemblySymbols ' This should be done after we created/found all AssemblySymbols Dim missingAssemblies As Dictionary(Of AssemblyIdentity, MissingAssemblySymbol) = Nothing ' -1 for assembly being built Dim totalReferencedAssemblyCount = allAssemblyData.Length - 1 ' Setup bound references for newly created SourceAssemblySymbol Dim moduleReferences As ImmutableArray(Of ModuleReferences(Of AssemblySymbol)) = Nothing SetupReferencesForSourceAssembly(assemblySymbol, modules, totalReferencedAssemblyCount, bindingResult, missingAssemblies, moduleReferences) If newSymbols.Count > 0 Then ' Only if we detected that a referenced assembly refers to the assembly being built ' we allow the references to get a hold of the assembly being built. If hasCircularReference Then bindingResult(0).AssemblySymbol = assemblySymbol End If InitializeNewSymbols(newSymbols, assemblySymbol, allAssemblyData, bindingResult, missingAssemblies) End If If compilation._lazyAssemblySymbol Is Nothing Then SyncLock SymbolCacheAndReferenceManagerStateGuard If compilation._lazyAssemblySymbol Is Nothing Then If IsBound Then ' Another thread has finished constructing AssemblySymbol for another compilation that shares this manager. ' Drop the results and reuse the symbols that were created for the other compilation. Return False End If UpdateSymbolCacheNoLock(newSymbols, allAssemblyData, bindingResult) InitializeNoLock( referencedAssembliesMap, referencedModulesMap, boundReferenceDirectiveMap, boundReferenceDirectives, explicitReferences, implicitReferenceResolutions, hasCircularReference, resolutionDiagnostics.ToReadOnly(), If(corLibrary Is assemblySymbol, Nothing, corLibrary), modules, moduleReferences, assemblySymbol.SourceModule.GetReferencedAssemblySymbols(), aliasesOfReferencedAssemblies, assemblySymbol.SourceModule.GetUnifiedAssemblies(), mergedAssemblyReferencesMapOpt) ' Make sure that the given compilation holds on this instance of reference manager. Debug.Assert(compilation._referenceManager Is Me OrElse hasCircularReference) compilation._referenceManager = Me ' Finally, publish the source symbol after all data have been written. ' Once lazyAssemblySymbol is non-null other readers might start reading the data written above. compilation._lazyAssemblySymbol = assemblySymbol End If End SyncLock End If Return True Finally resolutionDiagnostics.Free() assemblyReferencesBySimpleName.Free() End Try End Function Private Shared Sub InitializeNewSymbols(newSymbols As List(Of Integer), assemblySymbol As SourceAssemblySymbol, assemblies As ImmutableArray(Of AssemblyData), bindingResult As BoundInputAssembly(), missingAssemblies As Dictionary(Of AssemblyIdentity, MissingAssemblySymbol)) Debug.Assert(newSymbols.Count > 0) Dim corLibrary = assemblySymbol.CorLibrary Debug.Assert(corLibrary IsNot Nothing) For Each i As Integer In newSymbols Dim compilationData = TryCast(assemblies(i), AssemblyDataForCompilation) If compilationData IsNot Nothing Then SetupReferencesForRetargetingAssembly(bindingResult, i, missingAssemblies, sourceAssemblyDebugOnly:=assemblySymbol) Else Dim fileData = DirectCast(assemblies(i), AssemblyDataForFile) SetupReferencesForFileAssembly(fileData, bindingResult, i, missingAssemblies, sourceAssemblyDebugOnly:=assemblySymbol) End If Next Dim linkedReferencedAssembliesBuilder = ArrayBuilder(Of AssemblySymbol).GetInstance() ' Setup CorLibrary and NoPia stuff for newly created assemblies For Each i As Integer In newSymbols If assemblies(i).ContainsNoPiaLocalTypes Then bindingResult(i).AssemblySymbol.SetNoPiaResolutionAssemblies( assemblySymbol.Modules(0).GetReferencedAssemblySymbols()) End If ' Setup linked referenced assemblies. linkedReferencedAssembliesBuilder.Clear() If assemblies(i).IsLinked Then linkedReferencedAssembliesBuilder.Add(bindingResult(i).AssemblySymbol) End If For Each referenceBinding In bindingResult(i).ReferenceBinding If referenceBinding.IsBound AndAlso assemblies(referenceBinding.DefinitionIndex).IsLinked Then linkedReferencedAssembliesBuilder.Add( bindingResult(referenceBinding.DefinitionIndex).AssemblySymbol) End If Next If linkedReferencedAssembliesBuilder.Count > 0 Then linkedReferencedAssembliesBuilder.RemoveDuplicates() bindingResult(i).AssemblySymbol.SetLinkedReferencedAssemblies(linkedReferencedAssembliesBuilder.ToImmutable()) End If bindingResult(i).AssemblySymbol.SetCorLibrary(corLibrary) Next linkedReferencedAssembliesBuilder.Free() If missingAssemblies IsNot Nothing Then For Each missingAssembly In missingAssemblies.Values missingAssembly.SetCorLibrary(corLibrary) Next End If End Sub Private Sub UpdateSymbolCacheNoLock(newSymbols As List(Of Integer), assemblies As ImmutableArray(Of AssemblyData), bindingResult As BoundInputAssembly()) ' Add new assembly symbols into the cache For Each i As Integer In newSymbols Dim compilationData = TryCast(assemblies(i), AssemblyDataForCompilation) If compilationData IsNot Nothing Then compilationData.Compilation.CacheRetargetingAssemblySymbolNoLock(bindingResult(i).AssemblySymbol) Else Dim fileData = DirectCast(assemblies(i), AssemblyDataForFile) fileData.CachedSymbols.Add(bindingResult(i).AssemblySymbol) End If Next End Sub Private Shared Sub SetupReferencesForRetargetingAssembly( bindingResult() As BoundInputAssembly, bindingIndex As Integer, ByRef missingAssemblies As Dictionary(Of AssemblyIdentity, MissingAssemblySymbol), sourceAssemblyDebugOnly As SourceAssemblySymbol ) Dim retargetingAssemblySymbol = DirectCast(bindingResult(bindingIndex).AssemblySymbol, Retargeting.RetargetingAssemblySymbol) Dim modules As ImmutableArray(Of ModuleSymbol) = retargetingAssemblySymbol.Modules Dim moduleCount = modules.Length Dim refsUsed As Integer = 0 For j As Integer = 0 To moduleCount - 1 Step 1 Dim referencedAssemblies As ImmutableArray(Of AssemblyIdentity) = retargetingAssemblySymbol.UnderlyingAssembly.Modules(j).GetReferencedAssemblies() ' For source module skip underlying linked references If j = 0 Then Dim underlyingReferencedAssemblySymbols As ImmutableArray(Of AssemblySymbol) = retargetingAssemblySymbol.UnderlyingAssembly.Modules(0).GetReferencedAssemblySymbols() Dim linkedUnderlyingReferences As Integer = 0 For Each asm As AssemblySymbol In underlyingReferencedAssemblySymbols If asm.IsLinked Then linkedUnderlyingReferences += 1 End If Next If linkedUnderlyingReferences > 0 Then Dim filteredReferencedAssemblies As AssemblyIdentity() = New AssemblyIdentity(referencedAssemblies.Length - linkedUnderlyingReferences - 1) {} Dim newIndex As Integer = 0 For k As Integer = 0 To underlyingReferencedAssemblySymbols.Length - 1 Step 1 If Not underlyingReferencedAssemblySymbols(k).IsLinked Then filteredReferencedAssemblies(newIndex) = referencedAssemblies(k) newIndex += 1 End If Next Debug.Assert(newIndex = filteredReferencedAssemblies.Length) referencedAssemblies = filteredReferencedAssemblies.AsImmutableOrNull() End If End If Dim refsCount As Integer = referencedAssemblies.Length Dim symbols(refsCount - 1) As AssemblySymbol Dim unifiedAssemblies As ArrayBuilder(Of UnifiedAssembly(Of AssemblySymbol)) = Nothing For k As Integer = 0 To refsCount - 1 Step 1 Dim referenceBinding = bindingResult(bindingIndex).ReferenceBinding(refsUsed + k) If referenceBinding.IsBound Then symbols(k) = GetAssemblyDefinitionSymbol(bindingResult, referenceBinding, unifiedAssemblies) Else symbols(k) = GetOrAddMissingAssemblySymbol(referencedAssemblies(k), missingAssemblies) End If Next Dim moduleReferences As New ModuleReferences(Of AssemblySymbol)(referencedAssemblies, symbols.AsImmutableOrNull(), unifiedAssemblies.AsImmutableOrEmpty()) modules(j).SetReferences(moduleReferences, sourceAssemblyDebugOnly) refsUsed += refsCount Next End Sub Private Shared Sub SetupReferencesForFileAssembly( fileData As AssemblyDataForFile, bindingResult() As BoundInputAssembly, bindingIndex As Integer, ByRef missingAssemblies As Dictionary(Of AssemblyIdentity, MissingAssemblySymbol), sourceAssemblyDebugOnly As SourceAssemblySymbol ) Dim peAssemblySymbol = DirectCast(bindingResult(bindingIndex).AssemblySymbol, Symbols.Metadata.PE.PEAssemblySymbol) Dim modules As ImmutableArray(Of ModuleSymbol) = peAssemblySymbol.Modules Dim moduleCount = modules.Length Dim refsUsed As Integer = 0 For j As Integer = 0 To moduleCount - 1 Step 1 Dim refsCount As Integer = fileData.Assembly.ModuleReferenceCounts(j) Dim names(refsCount - 1) As AssemblyIdentity Dim symbols(refsCount - 1) As AssemblySymbol fileData.AssemblyReferences.CopyTo(refsUsed, names, 0, refsCount) Dim unifiedAssemblies As ArrayBuilder(Of UnifiedAssembly(Of AssemblySymbol)) = Nothing For k As Integer = 0 To refsCount - 1 Step 1 Dim referenceBinding = bindingResult(bindingIndex).ReferenceBinding(refsUsed + k) If referenceBinding.IsBound Then symbols(k) = GetAssemblyDefinitionSymbol(bindingResult, referenceBinding, unifiedAssemblies) Else symbols(k) = GetOrAddMissingAssemblySymbol(names(k), missingAssemblies) End If Next Dim moduleReferences = New ModuleReferences(Of AssemblySymbol)(names.AsImmutableOrNull(), symbols.AsImmutableOrNull(), unifiedAssemblies.AsImmutableOrEmpty()) modules(j).SetReferences(moduleReferences, sourceAssemblyDebugOnly) refsUsed += refsCount Next End Sub Private Shared Sub SetupReferencesForSourceAssembly( sourceAssembly As SourceAssemblySymbol, modules As ImmutableArray(Of PEModule), totalReferencedAssemblyCount As Integer, bindingResult() As BoundInputAssembly, ByRef missingAssemblies As Dictionary(Of AssemblyIdentity, MissingAssemblySymbol), ByRef moduleReferences As ImmutableArray(Of ModuleReferences(Of AssemblySymbol)) ) Dim moduleSymbols = sourceAssembly.Modules Debug.Assert(moduleSymbols.Length = 1 + modules.Length) Dim moduleReferencesBuilder = If(moduleSymbols.Length > 1, ArrayBuilder(Of ModuleReferences(Of AssemblySymbol)).GetInstance(), Nothing) Dim refsUsed As Integer = 0 For moduleIndex As Integer = 0 To moduleSymbols.Length - 1 Step 1 Dim refsCount As Integer = If(moduleIndex = 0, totalReferencedAssemblyCount, modules(moduleIndex - 1).ReferencedAssemblies.Length) Dim identities(refsCount - 1) As AssemblyIdentity Dim symbols(refsCount - 1) As AssemblySymbol Dim unifiedAssemblies As ArrayBuilder(Of UnifiedAssembly(Of AssemblySymbol)) = Nothing For k As Integer = 0 To refsCount - 1 Step 1 Dim boundReference = bindingResult(0).ReferenceBinding(refsUsed + k) If boundReference.IsBound Then symbols(k) = GetAssemblyDefinitionSymbol(bindingResult, boundReference, unifiedAssemblies) Else symbols(k) = GetOrAddMissingAssemblySymbol(boundReference.ReferenceIdentity, missingAssemblies) End If identities(k) = boundReference.ReferenceIdentity Next Dim references = New ModuleReferences(Of AssemblySymbol)(identities.AsImmutableOrNull(), symbols.AsImmutableOrNull(), unifiedAssemblies.AsImmutableOrEmpty()) If moduleIndex > 0 Then moduleReferencesBuilder.Add(references) End If moduleSymbols(moduleIndex).SetReferences(references, sourceAssembly) refsUsed += refsCount Next moduleReferences = moduleReferencesBuilder.ToImmutableOrEmptyAndFree() End Sub Private Shared Function GetAssemblyDefinitionSymbol(bindingResult As BoundInputAssembly(), referenceBinding As AssemblyReferenceBinding, ByRef unifiedAssemblies As ArrayBuilder(Of UnifiedAssembly(Of AssemblySymbol))) As AssemblySymbol Debug.Assert(referenceBinding.IsBound) Dim assembly = bindingResult(referenceBinding.DefinitionIndex).AssemblySymbol Debug.Assert(assembly IsNot Nothing) If referenceBinding.VersionDifference <> 0 Then If unifiedAssemblies Is Nothing Then unifiedAssemblies = New ArrayBuilder(Of UnifiedAssembly(Of AssemblySymbol))() End If unifiedAssemblies.Add(New UnifiedAssembly(Of AssemblySymbol)(assembly, referenceBinding.ReferenceIdentity)) End If Return assembly End Function Private Shared Function GetOrAddMissingAssemblySymbol( identity As AssemblyIdentity, ByRef missingAssemblies As Dictionary(Of AssemblyIdentity, MissingAssemblySymbol) ) As MissingAssemblySymbol Dim missingAssembly As MissingAssemblySymbol = Nothing If missingAssemblies Is Nothing Then missingAssemblies = New Dictionary(Of AssemblyIdentity, MissingAssemblySymbol)() ElseIf missingAssemblies.TryGetValue(identity, missingAssembly) Then Return missingAssembly End If missingAssembly = New MissingAssemblySymbol(identity) missingAssemblies.Add(identity, missingAssembly) Return missingAssembly End Function Private MustInherit Class AssemblyDataForMetadataOrCompilation Inherits AssemblyData Private _assemblies As ImmutableArray(Of AssemblySymbol) Private ReadOnly _identity As AssemblyIdentity Private ReadOnly _referencedAssemblies As ImmutableArray(Of AssemblyIdentity) Private ReadOnly _embedInteropTypes As Boolean Protected Sub New(identity As AssemblyIdentity, referencedAssemblies As ImmutableArray(Of AssemblyIdentity), embedInteropTypes As Boolean) Debug.Assert(identity IsNot Nothing) Debug.Assert(Not referencedAssemblies.IsDefault) _embedInteropTypes = embedInteropTypes _identity = identity _referencedAssemblies = referencedAssemblies End Sub Friend MustOverride Function CreateAssemblySymbol() As AssemblySymbol Public Overrides ReadOnly Property Identity As AssemblyIdentity Get Return _identity End Get End Property Public Overrides ReadOnly Property AvailableSymbols As ImmutableArray(Of AssemblySymbol) Get If (_assemblies.IsDefault) Then Dim assemblies = ArrayBuilder(Of AssemblySymbol).GetInstance() ' This should be done lazy because while we creating ' instances of this type, creation of new SourceAssembly symbols ' might change the set of available AssemblySymbols. AddAvailableSymbols(assemblies) _assemblies = assemblies.ToImmutableAndFree() End If Return _assemblies End Get End Property Protected MustOverride Sub AddAvailableSymbols(assemblies As ArrayBuilder(Of AssemblySymbol)) Public Overrides ReadOnly Property AssemblyReferences As ImmutableArray(Of AssemblyIdentity) Get Return _referencedAssemblies End Get End Property Public Overrides Function BindAssemblyReferences(assemblies As MultiDictionary(Of String, (DefinitionData As AssemblyData, DefinitionIndex As Integer)), assemblyIdentityComparer As AssemblyIdentityComparer) As AssemblyReferenceBinding() Return ResolveReferencedAssemblies(_referencedAssemblies, assemblies, resolveAgainstAssemblyBeingBuilt:=True, assemblyIdentityComparer:=assemblyIdentityComparer) End Function Public NotOverridable Overrides ReadOnly Property IsLinked As Boolean Get Return _embedInteropTypes End Get End Property End Class Private NotInheritable Class AssemblyDataForFile Inherits AssemblyDataForMetadataOrCompilation Public ReadOnly Assembly As PEAssembly ''' <summary> ''' Guarded by <see cref="CommonReferenceManager.SymbolCacheAndReferenceManagerStateGuard"/>. ''' </summary> Public ReadOnly CachedSymbols As WeakList(Of IAssemblySymbolInternal) Public ReadOnly DocumentationProvider As DocumentationProvider ''' <summary> ''' Import options of the compilation being built. ''' </summary> Private ReadOnly _compilationImportOptions As MetadataImportOptions ' This is the name of the compilation that is being built. ' This should be the assembly name w/o the extension. It is ' used to compute whether or not it is possible that this ' assembly will give friend access to the compilation. Private ReadOnly _sourceAssemblySimpleName As String Private _internalsVisibleComputed As Boolean = False Private _internalsPotentiallyVisibleToCompilation As Boolean = False Public Sub New(assembly As PEAssembly, cachedSymbols As WeakList(Of IAssemblySymbolInternal), embedInteropTypes As Boolean, documentationProvider As DocumentationProvider, sourceAssemblySimpleName As String, compilationImportOptions As MetadataImportOptions) MyBase.New(assembly.Identity, assembly.AssemblyReferences, embedInteropTypes) Debug.Assert(documentationProvider IsNot Nothing) Debug.Assert(cachedSymbols IsNot Nothing) Me.CachedSymbols = cachedSymbols Me.Assembly = assembly Me.DocumentationProvider = documentationProvider _compilationImportOptions = compilationImportOptions _sourceAssemblySimpleName = sourceAssemblySimpleName End Sub Friend Overrides Function CreateAssemblySymbol() As AssemblySymbol Return New PEAssemblySymbol(Assembly, DocumentationProvider, IsLinked, EffectiveImportOptions) End Function Friend ReadOnly Property InternalsMayBeVisibleToCompilation As Boolean Get If Not _internalsVisibleComputed Then _internalsPotentiallyVisibleToCompilation = InternalsMayBeVisibleToAssemblyBeingCompiled(_sourceAssemblySimpleName, Assembly) _internalsVisibleComputed = True End If Return _internalsPotentiallyVisibleToCompilation End Get End Property Friend ReadOnly Property EffectiveImportOptions As MetadataImportOptions Get If InternalsMayBeVisibleToCompilation AndAlso _compilationImportOptions = MetadataImportOptions.Public Then Return MetadataImportOptions.Internal End If Return _compilationImportOptions End Get End Property Protected Overrides Sub AddAvailableSymbols(assemblies As ArrayBuilder(Of AssemblySymbol)) Dim internalsMayBeVisibleToCompilation = Me.InternalsMayBeVisibleToCompilation ' accessing cached symbols requires a lock SyncLock SymbolCacheAndReferenceManagerStateGuard For Each assemblySymbol In CachedSymbols Dim peAssembly = TryCast(assemblySymbol, PEAssemblySymbol) If IsMatchingAssembly(peAssembly) Then assemblies.Add(peAssembly) End If Next End SyncLock End Sub Public Overrides Function IsMatchingAssembly(candidateAssembly As AssemblySymbol) As Boolean Return IsMatchingAssembly(TryCast(candidateAssembly, PEAssemblySymbol)) End Function Private Overloads Function IsMatchingAssembly(peAssembly As PEAssemblySymbol) As Boolean If peAssembly Is Nothing Then Return False End If If peAssembly.Assembly IsNot Me.Assembly Then Return False End If If EffectiveImportOptions <> peAssembly.PrimaryModule.ImportOptions Then Return False End If ' TODO (tomat): ' We shouldn't need to compare documentation providers. All symbols in the cachedSymbols list ' should share the same provider - as they share the same metadata. ' Removing the Equals call also avoids calling user code while holding a lock. If Not peAssembly.DocumentationProvider.Equals(DocumentationProvider) Then Return False End If Return True End Function Public Overrides ReadOnly Property ContainsNoPiaLocalTypes() As Boolean Get Return Assembly.ContainsNoPiaLocalTypes() End Get End Property Public Overrides ReadOnly Property DeclaresTheObjectClass As Boolean Get Return Assembly.DeclaresTheObjectClass End Get End Property Public Overrides ReadOnly Property SourceCompilation As Compilation Get Return Nothing End Get End Property End Class Private NotInheritable Class AssemblyDataForCompilation Inherits AssemblyDataForMetadataOrCompilation Public ReadOnly Compilation As VisualBasicCompilation Public Sub New(compilation As VisualBasicCompilation, embedInteropTypes As Boolean) MyBase.New(compilation.Assembly.Identity, GetReferencedAssemblies(compilation), embedInteropTypes) Debug.Assert(compilation IsNot Nothing) Me.Compilation = compilation End Sub Private Shared Function GetReferencedAssemblies(compilation As VisualBasicCompilation) As ImmutableArray(Of AssemblyIdentity) ' Collect information about references Dim refs = ArrayBuilder(Of AssemblyIdentity).GetInstance() Dim modules = compilation.Assembly.Modules ' Filter out linked assemblies referenced by the source module. Dim sourceReferencedAssemblies = modules(0).GetReferencedAssemblies() Dim sourceReferencedAssemblySymbols = modules(0).GetReferencedAssemblySymbols() Debug.Assert(sourceReferencedAssemblies.Length = sourceReferencedAssemblySymbols.Length) For i = 0 To sourceReferencedAssemblies.Length - 1 If Not sourceReferencedAssemblySymbols(i).IsLinked Then refs.Add(sourceReferencedAssemblies(i)) End If Next For i = 1 To modules.Length - 1 refs.AddRange(modules(i).GetReferencedAssemblies()) Next Return refs.ToImmutableAndFree() End Function Friend Overrides Function CreateAssemblySymbol() As AssemblySymbol Return New RetargetingAssemblySymbol(Compilation.SourceAssembly, IsLinked) End Function Protected Overrides Sub AddAvailableSymbols(assemblies As ArrayBuilder(Of AssemblySymbol)) assemblies.Add(Compilation.Assembly) ' accessing cached symbols requires a lock SyncLock SymbolCacheAndReferenceManagerStateGuard Compilation.AddRetargetingAssemblySymbolsNoLock(assemblies) End SyncLock End Sub Public Overrides Function IsMatchingAssembly(candidateAssembly As AssemblySymbol) As Boolean Dim retargeting = TryCast(candidateAssembly, RetargetingAssemblySymbol) Dim asm As AssemblySymbol If retargeting IsNot Nothing Then asm = retargeting.UnderlyingAssembly Else asm = TryCast(candidateAssembly, SourceAssemblySymbol) End If Debug.Assert(TypeOf asm IsNot RetargetingAssemblySymbol) Return asm Is Compilation.Assembly End Function Public Overrides ReadOnly Property ContainsNoPiaLocalTypes As Boolean Get Return Compilation.MightContainNoPiaLocalTypes() End Get End Property Public Overrides ReadOnly Property DeclaresTheObjectClass As Boolean Get Return Compilation.DeclaresTheObjectClass End Get End Property Public Overrides ReadOnly Property SourceCompilation As Compilation Get Return Compilation End Get End Property End Class ''' <summary> ''' For testing purposes only. ''' </summary> Friend Shared Function IsSourceAssemblySymbolCreated(compilation As VisualBasicCompilation) As Boolean Return compilation._lazyAssemblySymbol IsNot Nothing End Function ''' <summary> ''' For testing purposes only. ''' </summary> Friend Shared Function IsReferenceManagerInitialized(compilation As VisualBasicCompilation) As Boolean Return compilation._referenceManager.IsBound End Function End Class End Class End Namespace