File: Emit\EditAndContinue\VisualBasicDefinitionMap.vb
Web Access
Project: src\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' 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.Reflection.Metadata
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.CodeGen
Imports Microsoft.CodeAnalysis.Emit
Imports Microsoft.CodeAnalysis.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE
 
Namespace Microsoft.CodeAnalysis.VisualBasic.Emit
    ''' <summary>
    ''' Matches symbols from an assembly in one compilation to
    ''' the corresponding assembly in another. Assumes that only
    ''' one assembly has changed between the two compilations.
    ''' </summary>
    Friend NotInheritable Class VisualBasicDefinitionMap
        Inherits DefinitionMap
 
        Private ReadOnly _metadataDecoder As MetadataDecoder
        Private ReadOnly _previousSourceToMetadata As VisualBasicSymbolMatcher
        Private ReadOnly _sourceToMetadata As VisualBasicSymbolMatcher
        Private ReadOnly _sourceToPrevious As VisualBasicSymbolMatcher
 
        Public Sub New(edits As IEnumerable(Of SemanticEdit),
                       metadataDecoder As MetadataDecoder,
                       previousSourceToMetadata As VisualBasicSymbolMatcher,
                       sourceToMetadata As VisualBasicSymbolMatcher,
                       sourceToPreviousSource As VisualBasicSymbolMatcher,
                       baseline As EmitBaseline)
 
            MyBase.New(edits, baseline)
 
            Debug.Assert(metadataDecoder IsNot Nothing)
            Debug.Assert(sourceToMetadata IsNot Nothing)
 
            _metadataDecoder = metadataDecoder
            _previousSourceToMetadata = previousSourceToMetadata
            _sourceToMetadata = sourceToMetadata
            _sourceToPrevious = If(sourceToPreviousSource, sourceToMetadata)
        End Sub
 
        Public Overrides ReadOnly Property SourceToMetadataSymbolMatcher As SymbolMatcher
            Get
                Return _sourceToMetadata
            End Get
        End Property
 
        Public Overrides ReadOnly Property SourceToPreviousSymbolMatcher As SymbolMatcher
            Get
                Return _sourceToPrevious
            End Get
        End Property
 
        Public Overrides ReadOnly Property PreviousSourceToMetadataSymbolMatcher As SymbolMatcher
            Get
                Return _previousSourceToMetadata
            End Get
        End Property
 
        Protected Overrides Function GetISymbolInternalOrNull(symbol As ISymbol) As ISymbolInternal
            Return TryCast(symbol, Symbol)
        End Function
 
        Friend Overrides ReadOnly Property MessageProvider As CommonMessageProvider
            Get
                Return VisualBasic.MessageProvider.Instance
            End Get
        End Property
 
        Protected Overrides Function GetLambdaSyntaxFacts() As LambdaSyntaxFacts
            Return VisualBasicLambdaSyntaxFacts.Instance
        End Function
 
        Private Shared Function IsParentDisplayClassFieldName(name As String) As Boolean
            Return name.StartsWith(GeneratedNameConstants.HoistedSpecialVariablePrefix & GeneratedNameConstants.ClosureVariablePrefix, StringComparison.Ordinal)
        End Function
 
        Friend Function TryGetAnonymousTypeName(template As AnonymousTypeManager.AnonymousTypeOrDelegateTemplateSymbol, <Out> ByRef name As String, <Out> ByRef index As Integer) As Boolean
            Return _sourceToPrevious.TryGetAnonymousTypeName(template, name, index)
        End Function
 
        Protected Overrides Function TryGetStateMachineType(methodHandle As MethodDefinitionHandle) As ITypeSymbolInternal
            Dim typeName As String = Nothing
            If _metadataDecoder.Module.HasStateMachineAttribute(methodHandle, typeName) Then
                Return _metadataDecoder.GetTypeSymbolForSerializedType(typeName)
            End If
 
            Return Nothing
        End Function
 
        Protected Overrides Function GetMethodSymbol(methodHandle As MethodDefinitionHandle) As IMethodSymbolInternal
            Return DirectCast(_metadataDecoder.GetSymbolForILToken(methodHandle), IMethodSymbolInternal)
        End Function
 
        Protected Overrides Sub GetStateMachineFieldMapFromMetadata(stateMachineType As ITypeSymbolInternal,
                                                                    localSlotDebugInfo As ImmutableArray(Of LocalSlotDebugInfo),
                                                                    <Out> ByRef hoistedLocalMap As IReadOnlyDictionary(Of EncHoistedLocalInfo, Integer),
                                                                    <Out> ByRef awaiterMap As IReadOnlyDictionary(Of Cci.ITypeReference, Integer),
                                                                    <Out> ByRef awaiterSlotCount As Integer)
            ' we are working with PE symbols
            Debug.Assert(TypeOf stateMachineType.ContainingAssembly Is PEAssemblySymbol)
 
            Dim hoistedLocals = New Dictionary(Of EncHoistedLocalInfo, Integer)()
            Dim awaiters = New Dictionary(Of Cci.ITypeReference, Integer)(DirectCast(Cci.SymbolEquivalentEqualityComparer.Instance, IEqualityComparer(Of Cci.IReference)))
            Dim maxAwaiterSlotIndex = -1
 
            For Each member In DirectCast(stateMachineType, TypeSymbol).GetMembers()
                If member.Kind = SymbolKind.Field Then
                    Dim name = member.Name
                    Dim slotIndex As Integer
 
                    Select Case GeneratedNameParser.GetKind(name)
                        Case GeneratedNameKind.StateMachineAwaiterField
 
                            If GeneratedNameParser.TryParseSlotIndex(GeneratedNameConstants.StateMachineAwaiterFieldPrefix, name, slotIndex) Then
                                Dim field = DirectCast(member, FieldSymbol)
 
                                ' Correct metadata won't contain duplicates, but malformed might, ignore the duplicate:
                                awaiters(DirectCast(field.Type.GetCciAdapter(), Cci.ITypeReference)) = slotIndex
 
                                If slotIndex > maxAwaiterSlotIndex Then
                                    maxAwaiterSlotIndex = slotIndex
                                End If
                            End If
 
                        Case GeneratedNameKind.HoistedSynthesizedLocalField,
                             GeneratedNameKind.HoistedWithLocalPrefix,
                             GeneratedNameKind.StateMachineHoistedUserVariableOrDisplayClassField
 
                            Dim variableName As String = Nothing
                            If GeneratedNameParser.TryParseSlotIndex(GeneratedNameConstants.HoistedSynthesizedLocalPrefix, name, slotIndex) OrElse
                               GeneratedNameParser.TryParseSlotIndex(GeneratedNameConstants.HoistedWithLocalPrefix, name, slotIndex) OrElse
                               GeneratedNameParser.TryParseStateMachineHoistedUserVariableOrDisplayClassName(name, variableName, slotIndex) Then
                                Dim field = DirectCast(member, FieldSymbol)
                                If slotIndex >= localSlotDebugInfo.Length Then
                                    ' Invalid metadata
                                    Continue For
                                End If
 
                                Dim key = New EncHoistedLocalInfo(localSlotDebugInfo(slotIndex), DirectCast(field.Type.GetCciAdapter(), Cci.ITypeReference))
 
                                ' Correct metadata won't contain duplicates, but malformed might, ignore the duplicate:
                                hoistedLocals(key) = slotIndex
                            End If
                    End Select
                End If
            Next
 
            hoistedLocalMap = hoistedLocals
            awaiterMap = awaiters
            awaiterSlotCount = maxAwaiterSlotIndex + 1
        End Sub
 
        Protected Overrides Function GetLocalSlotMapFromMetadata(handle As StandaloneSignatureHandle, debugInfo As EditAndContinueMethodDebugInformation) As ImmutableArray(Of EncLocalInfo)
            Debug.Assert(Not handle.IsNil)
 
            Dim localInfos = _metadataDecoder.GetLocalsOrThrow(handle)
            Dim result = CreateLocalSlotMap(debugInfo, localInfos)
            Debug.Assert(result.Length = localInfos.Length)
            Return result
        End Function
 
        ''' <summary>
        ''' Match local declarations to names to generate a map from
        ''' declaration to local slot. The names are indexed by slot And the
        ''' assumption Is that declarations are in the same order as slots.
        ''' </summary>
        Private Shared Function CreateLocalSlotMap(
            methodEncInfo As EditAndContinueMethodDebugInformation,
            slotMetadata As ImmutableArray(Of LocalInfo(Of TypeSymbol))) As ImmutableArray(Of EncLocalInfo)
 
            Dim result(slotMetadata.Length - 1) As EncLocalInfo
 
            Dim localSlots = methodEncInfo.LocalSlots
            If Not localSlots.IsDefault Then
 
                ' In case of corrupted PDB or metadata, these lengths might Not match.
                ' Let's guard against such case.
                Dim slotCount = Math.Min(localSlots.Length, slotMetadata.Length)
 
                Dim map = New Dictionary(Of EncLocalInfo, Integer)()
 
                For slotIndex = 0 To slotCount - 1
 
                    Dim slot = localSlots(slotIndex)
                    If slot.SynthesizedKind.IsLongLived() Then
                        Dim metadata = slotMetadata(slotIndex)
 
                        ' We do Not emit custom modifiers on locals so ignore the
                        ' previous version of the local if it had custom modifiers.
                        If metadata.CustomModifiers.IsDefaultOrEmpty Then
                            Dim local = New EncLocalInfo(slot, DirectCast(metadata.Type.GetCciAdapter(), Cci.ITypeReference), metadata.Constraints, metadata.SignatureOpt)
                            map.Add(local, slotIndex)
                        End If
                    End If
                Next
 
                For Each pair In map
                    result(pair.Value) = pair.Key
                Next
            End If
 
            ' Populate any remaining locals that were Not matched to source.
            For i = 0 To result.Length - 1
                If result(i).IsDefault Then
                    result(i) = New EncLocalInfo(slotMetadata(i).SignatureOpt)
                End If
            Next
 
            Return ImmutableArray.Create(result)
        End Function
 
        Protected Overrides Function TryParseDisplayClassOrLambdaName(
            name As String,
            <Out> ByRef suffixIndex As Integer,
            <Out> ByRef idSeparator As Char,
            <Out> ByRef isDisplayClass As Boolean,
            <Out> ByRef isDisplayClassParentField As Boolean,
            <Out> ByRef hasDebugIds As Boolean) As Boolean
 
            idSeparator = GeneratedNameConstants.IdSeparator
 
            isDisplayClass = name.StartsWith(GeneratedNameConstants.DisplayClassPrefix, StringComparison.Ordinal)
            If isDisplayClass Then
                suffixIndex = GeneratedNameConstants.DisplayClassPrefix.Length
                isDisplayClassParentField = False
                hasDebugIds = name.Length > suffixIndex
                Return True
            End If
 
            If name.StartsWith(GeneratedNameConstants.LambdaMethodNamePrefix, StringComparison.Ordinal) Then
                suffixIndex = GeneratedNameConstants.LambdaMethodNamePrefix.Length
                isDisplayClassParentField = False
                hasDebugIds = name.Length > suffixIndex
                Return True
            End If
 
            If IsParentDisplayClassFieldName(name) Then
                suffixIndex = -1
                isDisplayClassParentField = True
                hasDebugIds = False
                Return True
            End If
 
            Return False
        End Function
    End Class
End Namespace