File: Emit\EditAndContinue\EditAndContinueTestBase.vb
Web Access
Project: src\src\Compilers\VisualBasic\Test\Emit\Microsoft.CodeAnalysis.VisualBasic.Emit.UnitTests.vbproj (Microsoft.CodeAnalysis.VisualBasic.Emit.UnitTests)
' 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.IO
Imports System.Reflection.Metadata
Imports System.Reflection.Metadata.Ecma335
Imports System.Runtime.CompilerServices
Imports System.Xml.Linq
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.EditAndContinue.UnitTests
Imports Microsoft.CodeAnalysis.Emit
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Emit
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Microsoft.Metadata.Tools
Imports Roslyn.Test.Utilities
 
Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests
    Public Class EditAndContinueTestBase
        Inherits BasicTestBase
 
        ' PDB reader can only be accessed from a single thread, so avoid concurrent compilation:
        Friend Shared ReadOnly ComSafeDebugDll As VisualBasicCompilationOptions = TestOptions.DebugDll.WithConcurrentBuild(False)
 
        Protected Shared ReadOnly ValueTupleRefs As MetadataReference() = {SystemRuntimeFacadeRef, ValueTupleRef}
 
        Friend Shared ReadOnly EmptyLocalsProvider As Func(Of MethodDefinitionHandle, EditAndContinueMethodDebugInformation) = Function(token) Nothing
 
        Friend Shared Function Visualize(baseline As ModuleMetadata, ParamArray deltas As PinnedMetadata()) As String
            Dim result = New StringWriter()
            Dim visualizer = New MetadataVisualizer({baseline.MetadataReader}.Concat(deltas.Select(Function(d) d.Reader)).ToArray(), result)
            visualizer.VisualizeAllGenerations()
            Return result.ToString()
        End Function
 
        Public Shared Function CreateInitialBaseline(compilation As Compilation, [module] As ModuleMetadata, debugInformationProvider As Func(Of MethodDefinitionHandle, EditAndContinueMethodDebugInformation)) As EmitBaseline
            Return EditAndContinueTestUtilities.CreateInitialBaseline(compilation, [module], debugInformationProvider)
        End Function
 
        Friend Shared Function MarkedSource(source As XElement, Optional fileName As String = "", Optional options As VisualBasicParseOptions = Nothing) As SourceWithMarkedNodes
            Return New SourceWithMarkedNodes(WithWindowsLineBreaks(source.Value), Function(s) Parse(s, fileName, options), Function(s) CInt(GetType(SyntaxKind).GetField(s).GetValue(Nothing)))
        End Function
 
        Friend Shared Function MarkedSource(source As String, Optional fileName As String = "", Optional options As VisualBasicParseOptions = Nothing) As SourceWithMarkedNodes
            Return New SourceWithMarkedNodes(WithWindowsLineBreaks(source), Function(s) Parse(s, fileName, options), Function(s) CInt(GetType(SyntaxKind).GetField(s).GetValue(Nothing)))
        End Function
 
        Friend Shared Function GetSyntaxMapFromMarkers(source0 As SourceWithMarkedNodes, source1 As SourceWithMarkedNodes) As Func(Of SyntaxNode, SyntaxNode)
            Return SourceWithMarkedNodes.GetSyntaxMap(source0, source1)
        End Function
 
        Friend Shared Function Edit(
            kind As SemanticEditKind,
            symbolProvider As Func(Of Compilation, ISymbol),
            Optional newSymbolProvider As Func(Of Compilation, ISymbol) = Nothing,
            Optional rudeEdits As Func(Of SyntaxNode, RuntimeRudeEdit?) = Nothing,
            Optional preserveLocalVariables As Boolean = False) As SemanticEditDescription
            Return New SemanticEditDescription(kind, symbolProvider, newSymbolProvider, rudeEdits, preserveLocalVariables)
        End Function
 
        Friend Function ToLocalInfo(local As Cci.ILocalDefinition) As ILVisualizer.LocalInfo
            Dim signature = local.Signature
            If signature Is Nothing Then
                Return New ILVisualizer.LocalInfo(local.Name, local.Type, local.IsPinned, local.IsReference)
            Else
                ' Decode simple types only.
                Dim typeName = If(signature.Length = 1, GetTypeName(CType(signature(0), SignatureTypeCode)), Nothing)
                Return New ILVisualizer.LocalInfo(Nothing, If(typeName, "[unchanged]"), False, False)
            End If
        End Function
 
        Private Function GetTypeName(typeCode As SignatureTypeCode) As String
            Select Case typeCode
                Case SignatureTypeCode.Boolean
                    Return "Boolean"
                Case SignatureTypeCode.Int32
                    Return "Integer"
                Case SignatureTypeCode.String
                    Return "String"
                Case SignatureTypeCode.Object
                    Return "Object"
                Case Else
                    Return Nothing
            End Select
        End Function
 
        Friend Shared Function GetAllLocals(compilation As VisualBasicCompilation, method As MethodSymbol) As ImmutableArray(Of LocalSymbol)
            Dim methodSyntax = method.DeclaringSyntaxReferences(0).GetSyntax().Parent
            Dim model = compilation.GetSemanticModel(methodSyntax.SyntaxTree)
            Dim locals = ArrayBuilder(Of LocalSymbol).GetInstance()
 
            For Each node In methodSyntax.DescendantNodes()
                If node.Kind = SyntaxKind.VariableDeclarator Then
                    For Each name In DirectCast(node, VariableDeclaratorSyntax).Names
                        Dim local = DirectCast(model.GetDeclaredSymbol(name), LocalSymbol)
                        locals.Add(local)
                    Next
                End If
            Next
 
            Return locals.ToImmutableAndFree()
        End Function
 
        Friend Shared Function GetAllLocals(compilation As VisualBasicCompilation, method As IMethodSymbol) As ImmutableArray(Of KeyValuePair(Of ILocalSymbol, Integer))
            Dim locals = GetAllLocals(compilation, DirectCast(method, MethodSymbol))
            Return locals.SelectAsArray(Function(local, index, arg) New KeyValuePair(Of ILocalSymbol, Integer)(local, index), DirectCast(Nothing, Object))
        End Function
 
        Friend Shared Function GetAllLocals(method As SourceMethodSymbol) As ImmutableArray(Of VisualBasicSyntaxNode)
            Dim names = From name In LocalVariableDeclaratorsCollector.GetDeclarators(method).OfType(Of ModifiedIdentifierSyntax)
                        Select DirectCast(name, VisualBasicSyntaxNode)
 
            Return names.AsImmutableOrEmpty
        End Function
 
        Friend Shared Function GetLocalName(node As SyntaxNode) As String
            If node.Kind = SyntaxKind.ModifiedIdentifier Then
                Return DirectCast(node, ModifiedIdentifierSyntax).Identifier.ToString()
            End If
 
            Throw New NotImplementedException()
        End Function
 
        Friend Shared Function GetSyntaxMapByKind(method As MethodSymbol, ParamArray kinds As SyntaxKind()) As Func(Of SyntaxNode, SyntaxNode)
            Return Function(node As SyntaxNode)
                       For Each k In kinds
                           If node.IsKind(k) Then
                               Return method.DeclaringSyntaxReferences.Single().SyntaxTree.GetRoot().DescendantNodes().Single(Function(n) n.IsKind(k))
                           End If
                       Next
 
                       Return Nothing
                   End Function
        End Function
 
        Friend Shared Function GetEquivalentNodesMap(method1 As MethodSymbol, method0 As MethodSymbol) As Func(Of SyntaxNode, SyntaxNode)
            Dim tree1 = method1.Locations(0).SourceTree
            Dim tree0 = method0.Locations(0).SourceTree
            Assert.NotEqual(tree1, tree0)
 
            Dim sourceMethod0 = DirectCast(method0, SourceMethodSymbol)
 
            Dim locals0 = GetAllLocals(sourceMethod0)
            Return Function(s As SyntaxNode)
                       Dim s1 = s
                       Assert.Equal(s1.SyntaxTree, tree1)
 
                       ' add mapping for result variable (it's declarator is the Function Statement)
                       If s.IsKind(SyntaxKind.FunctionStatement) Then
                           Assert.True(sourceMethod0.BlockSyntax.BlockStatement.IsKind(SyntaxKind.FunctionStatement))
                           Return sourceMethod0.BlockSyntax.BlockStatement
                       ElseIf s.IsKind(SyntaxKind.PropertyStatement) Then
                           Assert.True(sourceMethod0.BlockSyntax.IsKind(SyntaxKind.GetAccessorBlock))
                           Return DirectCast(sourceMethod0.BlockSyntax.Parent, PropertyBlockSyntax).PropertyStatement
                       ElseIf s.IsKind(SyntaxKind.EventStatement) Then
                           Assert.True(sourceMethod0.BlockSyntax.IsKind(SyntaxKind.AddHandlerAccessorBlock))
                           Return DirectCast(sourceMethod0.BlockSyntax.Parent, PropertyBlockSyntax).PropertyStatement
                       End If
 
                       For Each s0 In locals0
                           If Not SyntaxFactory.AreEquivalent(s0, s1) Then
                               Continue For
                           End If
                           ' Make sure the containing statements are the same.
                           Dim p0 = GetNearestStatement(s0)
                           Dim p1 = GetNearestStatement(s1)
                           If SyntaxFactory.AreEquivalent(p0, p1) Then
                               Return s0
                           End If
                       Next
                       Return Nothing
                   End Function
        End Function
 
        Friend Shared Function GetNearestStatement(node As SyntaxNode) As StatementSyntax
            While node IsNot Nothing
                Dim statement = TryCast(node, StatementSyntax)
                If statement IsNot Nothing Then
                    Return statement
                End If
 
                node = node.Parent
            End While
            Return Nothing
        End Function
 
        Friend Shared Function Row(rowNumber As Integer, table As TableIndex, operation As EditAndContinueOperation) As EditAndContinueLogEntry
            Return New EditAndContinueLogEntry(MetadataTokens.Handle(table, rowNumber), operation)
        End Function
 
        Friend Shared Function Handle(rowNumber As Integer, table As TableIndex) As EntityHandle
            Return MetadataTokens.Handle(table, rowNumber)
        End Function
 
        Friend Shared Function IsDefinition(kind As HandleKind) As Boolean
            Select Case kind
                Case HandleKind.AssemblyReference, HandleKind.ModuleReference, HandleKind.TypeReference, HandleKind.MemberReference, HandleKind.TypeSpecification, HandleKind.MethodSpecification
                    Return False
                Case Else
                    Return True
            End Select
        End Function
 
        ''' <summary>
        ''' Checks that the EncLog contains specified rows.
        ''' Any default values in the expected <paramref name="rows"/> are ignored to facilitate conditional code.
        ''' </summary>
        Friend Shared Sub CheckEncLog(reader As MetadataReader, ParamArray rows As EditAndContinueLogEntry())
            AssertEx.Equal(
                rows.Where(Function(r) r.Handle <> Nothing),
                reader.GetEditAndContinueLogEntries(), itemInspector:=AddressOf EditAndContinueTestUtilities.EncLogRowToString)
        End Sub
 
        ''' <summary>
        ''' Checks that the EncLog contains specified definition rows. References are ignored as they are usually not interesting to validate. They are emitted as needed.
        ''' Any default values in the expected <paramref name="rows"/> are ignored to facilitate conditional code.
        ''' </summary>
        Friend Shared Sub CheckEncLogDefinitions(reader As MetadataReader, ParamArray rows As EditAndContinueLogEntry())
            AssertEx.Equal(
                rows.Where(Function(r) r.Handle <> Nothing),
                reader.GetEditAndContinueLogEntries().Where(Function(entry) IsDefinition(entry.Handle.Kind)), itemInspector:=AddressOf EditAndContinueTestUtilities.EncLogRowToString)
        End Sub
 
        ''' <summary>
        ''' Checks that the EncMap contains specified handles.
        ''' Any default values in the expected <paramref name="handles"/> are ignored to facilitate conditional code.
        ''' </summary>
        Friend Shared Sub CheckEncMap(reader As MetadataReader, ParamArray [handles] As EntityHandle())
            AssertEx.Equal(
                [handles].Where(Function(h) h <> Nothing),
                reader.GetEditAndContinueMapEntries(), itemInspector:=AddressOf EditAndContinueTestUtilities.EncMapRowToString)
        End Sub
 
        ''' <summary>
        ''' Checks that the EncMap contains specified definition handles. References are ignored as they are usually Not interesting to validate. They are emitted as needed.
        ''' Any default values in the expected <paramref name="handles"/> are ignored to facilitate conditional code.
        ''' </summary>
        Friend Shared Sub CheckEncMapDefinitions(reader As MetadataReader, ParamArray [handles] As EntityHandle())
            AssertEx.Equal(
                [handles].Where(Function(h) h <> Nothing),
                reader.GetEditAndContinueMapEntries().Where(Function(e) IsDefinition(e.Kind)), itemInspector:=AddressOf EditAndContinueTestUtilities.EncMapRowToString)
        End Sub
 
        Friend Shared Sub CheckNames(reader As MetadataReader, [handles] As IEnumerable(Of StringHandle), ParamArray expectedNames As String())
            CheckNames({reader}, [handles], expectedNames)
        End Sub
 
        Friend Shared Sub CheckNames(readers As MetadataReader(), [handles] As IEnumerable(Of StringHandle), ParamArray expectedNames As String())
            Dim actualNames = readers.GetStrings([handles])
            AssertEx.Equal(expectedNames, actualNames)
        End Sub
 
        Public Shared Sub CheckNames(readers As IList(Of MetadataReader), typeHandles As IEnumerable(Of TypeDefinitionHandle), ParamArray expectedNames As String())
            CheckNames(readers, typeHandles, Function(reader, handle) reader.GetTypeDefinition(CType(handle, TypeDefinitionHandle)).Name, Function(handle) handle, expectedNames)
        End Sub
 
        Friend Shared Sub CheckNamesSorted(readers As MetadataReader(), [handles] As IEnumerable(Of StringHandle), ParamArray expectedNames As String())
            Dim actualNames = readers.GetStrings([handles])
            Array.Sort(actualNames)
            Array.Sort(expectedNames)
            AssertEx.Equal(expectedNames, actualNames)
        End Sub
 
        Private Shared Sub CheckNames(Of THandle)(
            readers As IList(Of MetadataReader),
            entityHandles As IEnumerable(Of THandle),
            getName As Func(Of MetadataReader, Handle, StringHandle),
            toHandle As Func(Of THandle, Handle),
            expectedNames As String())
            Dim aggregator = GetAggregator(readers)
 
            AssertEx.Equal(expectedNames, entityHandles.Select(
                Function(handle)
                    Dim typeGeneration As Integer
                    Dim genEntityHandle = aggregator.GetGenerationHandle(toHandle(handle), typeGeneration)
                    Dim nameHandle = getName(readers(typeGeneration), genEntityHandle)
 
                    Dim nameGeneration As Integer
                    Dim genNameHandle = CType(aggregator.GetGenerationHandle(nameHandle, nameGeneration), StringHandle)
                    Return readers(nameGeneration).GetString(genNameHandle)
                End Function))
        End Sub
 
        Public Shared Sub CheckBlobValue(readers As IList(Of MetadataReader), valueHandle As BlobHandle, expectedValue As Byte())
            Dim aggregator = GetAggregator(readers)
 
            Dim generation As Integer
            Dim genHandle = CType(aggregator.GetGenerationHandle(valueHandle, generation), BlobHandle)
            Dim attributeData = readers(generation).GetBlobBytes(genHandle)
            AssertEx.Equal(expectedValue, attributeData)
        End Sub
 
        Public Shared Sub CheckStringValue(readers As IList(Of MetadataReader), valueHandle As StringHandle, expectedValue As String)
            Dim aggregator = GetAggregator(readers)
 
            Dim generation As Integer
            Dim genHandle = CType(aggregator.GetGenerationHandle(valueHandle, generation), StringHandle)
            Dim attributeData = readers(generation).GetString(genHandle)
            AssertEx.Equal(expectedValue, attributeData)
        End Sub
 
        Public Shared Function GetAggregator(readers As IList(Of MetadataReader)) As MetadataAggregator
            Return New MetadataAggregator(readers(0), readers.Skip(1).ToArray())
        End Function
 
        Friend Shared Function CreateMatcher(fromCompilation As VisualBasicCompilation, toCompilation As VisualBasicCompilation) As VisualBasicSymbolMatcher
            Return New VisualBasicSymbolMatcher(
                fromCompilation.SourceAssembly,
                toCompilation.SourceAssembly,
                synthesizedTypes:=SynthesizedTypeMaps.Empty,
                otherSynthesizedMembersOpt:=Nothing,
                otherDeletedMembersOpt:=Nothing)
        End Function
    End Class
 
    Public Module EditAndContinueTestExtensions
        <Extension>
        Public Function WithSource(compilation As VisualBasicCompilation, newSource As String) As VisualBasicCompilation
            Return compilation.RemoveAllSyntaxTrees().AddSyntaxTrees(VisualBasicSyntaxTree.ParseText(newSource))
        End Function
 
        <Extension>
        Public Function WithSource(compilation As VisualBasicCompilation, newSource As XElement) As VisualBasicCompilation
            Return compilation.RemoveAllSyntaxTrees().AddSyntaxTrees(ToSourceTrees(newSource))
        End Function
 
        <Extension>
        Public Function WithSource(compilation As VisualBasicCompilation, newTree As SyntaxTree) As VisualBasicCompilation
            Return compilation.RemoveAllSyntaxTrees().AddSyntaxTrees(newTree)
        End Function
    End Module
End Namespace