File: Emit\EditAndContinue\EmitHelpers.vb
Web Access
Project: ..\..\..\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.IO
Imports System.Reflection.Metadata
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.CodeGen
Imports Microsoft.CodeAnalysis.Emit
Imports Microsoft.CodeAnalysis.PooledObjects
 
Namespace Microsoft.CodeAnalysis.VisualBasic.Emit
 
    Friend Module EmitHelpers
 
        Friend Function EmitDifference(
            compilation As VisualBasicCompilation,
            baseline As EmitBaseline,
            edits As IEnumerable(Of SemanticEdit),
            isAddedSymbol As Func(Of ISymbol, Boolean),
            metadataStream As Stream,
            ilStream As Stream,
            pdbStream As Stream,
            testData As CompilationTestData,
            cancellationToken As CancellationToken) As EmitDifferenceResult
 
            Dim pdbName = FileNameUtilities.ChangeExtension(compilation.SourceModule.Name, "pdb")
            Dim diagnostics = DiagnosticBag.GetInstance()
 
            Dim emitOpts = EmitOptions.Default.WithDebugInformationFormat(If(baseline.HasPortablePdb, DebugInformationFormat.PortablePdb, DebugInformationFormat.Pdb))
            Dim runtimeMDVersion = compilation.GetRuntimeMetadataVersion()
            Dim serializationProperties = compilation.ConstructModuleSerializationProperties(emitOpts, runtimeMDVersion, baseline.ModuleVersionId)
            Dim manifestResources = SpecializedCollections.EmptyEnumerable(Of ResourceDescription)()
 
            Dim moduleBeingBuilt As PEDeltaAssemblyBuilder
            Try
                moduleBeingBuilt = New PEDeltaAssemblyBuilder(
                    compilation.SourceAssembly,
                    emitOptions:=emitOpts,
                    outputKind:=compilation.Options.OutputKind,
                    serializationProperties:=serializationProperties,
                    manifestResources:=manifestResources,
                    previousGeneration:=baseline,
                    edits:=edits,
                    isAddedSymbol:=isAddedSymbol)
            Catch e As NotSupportedException
                ' TODO: https://github.com/dotnet/roslyn/issues/9004
                diagnostics.Add(ERRID.ERR_ModuleEmitFailure, NoLocation.Singleton, compilation.AssemblyName, e.Message)
                Return New EmitDifferenceResult(
                    success:=False,
                    diagnostics:=diagnostics.ToReadOnlyAndFree(),
                    baseline:=Nothing,
                    updatedMethods:=ImmutableArray(Of MethodDefinitionHandle).Empty,
                    changedTypes:=ImmutableArray(Of TypeDefinitionHandle).Empty)
            End Try
 
            If testData IsNot Nothing Then
                moduleBeingBuilt.SetTestData(testData)
            End If
 
            Dim definitionMap = moduleBeingBuilt.PreviousDefinitions
            Dim changes = moduleBeingBuilt.EncSymbolChanges
 
            Dim newBaseline As EmitBaseline = Nothing
            Dim updatedMethods = ArrayBuilder(Of MethodDefinitionHandle).GetInstance()
            Dim changedTypes = ArrayBuilder(Of TypeDefinitionHandle).GetInstance()
 
            If compilation.Compile(moduleBeingBuilt,
                                   emittingPdb:=True,
                                   diagnostics:=diagnostics,
                                   filterOpt:=Function(s) changes.RequiresCompilation(s.GetISymbol()),
                                   cancellationToken:=cancellationToken) Then
 
                ' Map the definitions from the previous compilation to the current compilation.
                ' This must be done after compiling above since synthesized definitions
                ' (generated when compiling method bodies) may be required.
                Dim mappedBaseline = MapToCompilation(compilation, moduleBeingBuilt)
 
                newBaseline = compilation.SerializeToDeltaStreams(
                    moduleBeingBuilt,
                    mappedBaseline,
                    definitionMap,
                    changes,
                    metadataStream,
                    ilStream,
                    pdbStream,
                    updatedMethods,
                    changedTypes,
                    diagnostics,
                    testData?.SymWriterFactory,
                    emitOpts.PdbFilePath,
                    cancellationToken)
            End If
 
            Return New EmitDifferenceResult(
                success:=newBaseline IsNot Nothing,
                diagnostics:=diagnostics.ToReadOnlyAndFree(),
                baseline:=newBaseline,
                updatedMethods:=updatedMethods.ToImmutableAndFree(),
                changedTypes:=changedTypes.ToImmutableAndFree())
        End Function
 
        Friend Function MapToCompilation(
            compilation As VisualBasicCompilation,
            moduleBeingBuilt As PEDeltaAssemblyBuilder) As EmitBaseline
 
            Dim previousGeneration = moduleBeingBuilt.PreviousGeneration
            Debug.Assert(previousGeneration.Compilation IsNot compilation)
 
            If previousGeneration.Ordinal = 0 Then
                ' Initial generation, nothing to map. (Since the initial generation
                ' is always loaded from metadata in the context of the current
                ' compilation, there's no separate mapping step.)
                Return previousGeneration
            End If
 
            Dim currentSynthesizedMembers = moduleBeingBuilt.GetAllSynthesizedMembers()
            Dim currentDeletedMembers = moduleBeingBuilt.EncSymbolChanges.GetAllDeletedMembers()
 
            ' Mapping from previous compilation to the current.
            Dim anonymousTypeMap = moduleBeingBuilt.GetAnonymousTypeMap()
            Dim sourceAssembly = DirectCast(previousGeneration.Compilation, VisualBasicCompilation).SourceAssembly
            Dim sourceContext = New EmitContext(DirectCast(previousGeneration.PEModuleBuilder, PEModuleBuilder), Nothing, New DiagnosticBag(), metadataOnly:=False, includePrivateMembers:=True)
            Dim otherContext = New EmitContext(moduleBeingBuilt, Nothing, New DiagnosticBag(), metadataOnly:=False, includePrivateMembers:=True)
 
            Dim matcher = New VisualBasicSymbolMatcher(
                anonymousTypeMap,
                sourceAssembly,
                sourceContext,
                compilation.SourceAssembly,
                otherContext,
                currentSynthesizedMembers,
                currentDeletedMembers)
 
            Dim mappedSynthesizedMembers = matcher.MapSynthesizedOrDeletedMembers(previousGeneration.SynthesizedMembers, currentSynthesizedMembers, isDeletedMemberMapping:=False)
            Dim mappedDeletedMembers = matcher.MapSynthesizedOrDeletedMembers(previousGeneration.DeletedMembers, currentDeletedMembers, isDeletedMemberMapping:=True)
 
            ' TODO can we reuse some data from the previous matcher?
            Dim matcherWithAllSynthesizedMembers = New VisualBasicSymbolMatcher(
                anonymousTypeMap,
                sourceAssembly,
                sourceContext,
                compilation.SourceAssembly,
                otherContext,
                mappedSynthesizedMembers,
                mappedDeletedMembers)
 
            Return matcherWithAllSynthesizedMembers.MapBaselineToCompilation(
                previousGeneration,
                compilation,
                moduleBeingBuilt,
                mappedSynthesizedMembers,
                mappedDeletedMembers)
        End Function
    End Module
End Namespace