public PEDeltaAssemblyBuilder(
            SourceAssemblySymbol sourceAssembly,
            string outputName,
            OutputKind outputKind,
            ModulePropertiesForSerialization serializationProperties,
            IEnumerable<ResourceDescription> manifestResources,
            Func<AssemblySymbol, AssemblyIdentity> assemblySymbolMapper,
            EmitBaseline previousGeneration,
            IEnumerable<SemanticEdit> edits)
            : base(sourceAssembly, outputName, outputKind, serializationProperties, manifestResources, assemblySymbolMapper, additionalTypes: ImmutableArray<NamedTypeSymbol>.Empty, metadataOnly:false)
        {
            var context = new EmitContext(this, null, new DiagnosticBag());
            var module = previousGeneration.OriginalMetadata;
            var compilation = sourceAssembly.DeclaringCompilation;
            var metadataAssembly = compilation.GetBoundReferenceManager().CreatePEAssemblyForAssemblyMetadata(AssemblyMetadata.Create(module), MetadataImportOptions.All);
            var metadataDecoder = new Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE.MetadataDecoder(metadataAssembly.PrimaryModule);

            previousGeneration = EnsureInitialized(previousGeneration, metadataDecoder);

            var matchToMetadata = new SymbolMatcher(previousGeneration.AnonymousTypeMap, sourceAssembly, context, metadataAssembly);

            SymbolMatcher matchToPrevious = null;
            if (previousGeneration.Ordinal > 0)
            {
                var previousAssembly = ((CSharpCompilation)previousGeneration.Compilation).SourceAssembly;
                var previousContext = new EmitContext((PEModuleBuilder)previousGeneration.PEModuleBuilder, null, new DiagnosticBag());
                matchToPrevious = new SymbolMatcher(previousGeneration.AnonymousTypeMap, sourceAssembly, context, previousAssembly, previousContext);
            }

            this.previousDefinitions = new CSharpDefinitionMap(previousGeneration.OriginalMetadata.Module, edits, metadataDecoder, matchToMetadata, matchToPrevious);
            this.previousGeneration = previousGeneration;
            this.changes = new SymbolChanges(this.previousDefinitions, edits);
        }
        public PEDeltaAssemblyBuilder(
            SourceAssemblySymbol sourceAssembly,
            EmitOptions emitOptions,
            OutputKind outputKind,
            Cci.ModulePropertiesForSerialization serializationProperties,
            IEnumerable<ResourceDescription> manifestResources,
            EmitBaseline previousGeneration,
            IEnumerable<SemanticEdit> edits,
            Func<ISymbol, bool> isAddedSymbol)
            : base(sourceAssembly, emitOptions, outputKind, serializationProperties, manifestResources, additionalTypes: ImmutableArray<NamedTypeSymbol>.Empty)
        {
            var initialBaseline = previousGeneration.InitialBaseline;
            var context = new EmitContext(this, null, new DiagnosticBag());

            // Hydrate symbols from initial metadata. Once we do so it is important to reuse these symbols across all generations,
            // in order for the symbol matcher to be able to use reference equality once it maps symbols to initial metadata.
            var metadataSymbols = GetOrCreateMetadataSymbols(initialBaseline, sourceAssembly.DeclaringCompilation);
            var metadataDecoder = (MetadataDecoder)metadataSymbols.MetadataDecoder;
            var metadataAssembly = (PEAssemblySymbol)metadataDecoder.ModuleSymbol.ContainingAssembly;

            var matchToMetadata = new CSharpSymbolMatcher(metadataSymbols.AnonymousTypes, sourceAssembly, context, metadataAssembly);

            CSharpSymbolMatcher matchToPrevious = null;
            if (previousGeneration.Ordinal > 0)
            {
                var previousAssembly = ((CSharpCompilation)previousGeneration.Compilation).SourceAssembly;
                var previousContext = new EmitContext((PEModuleBuilder)previousGeneration.PEModuleBuilder, null, new DiagnosticBag());

                matchToPrevious = new CSharpSymbolMatcher(
                    previousGeneration.AnonymousTypeMap,
                    sourceAssembly: sourceAssembly,
                    sourceContext: context,
                    otherAssembly: previousAssembly,
                    otherContext: previousContext,
                    otherSynthesizedMembersOpt: previousGeneration.SynthesizedMembers);
            }

            _previousDefinitions = new CSharpDefinitionMap(previousGeneration.OriginalMetadata.Module, edits, metadataDecoder, matchToMetadata, matchToPrevious);
            _previousGeneration = previousGeneration;
            _changes = new SymbolChanges(_previousDefinitions, edits, isAddedSymbol);

            // Workaround for https://github.com/dotnet/roslyn/issues/3192.
            // When compiling state machine we stash types of awaiters and state-machine hoisted variables,
            // so that next generation can look variables up and reuse their slots if possible.
            //
            // When we are about to allocate a slot for a lifted variable while compiling the next generation
            // we map its type to the previous generation and then check the slot types that we stashed earlier.
            // If the variable type matches we reuse it. In order to compare the previous variable type with the current one
            // both need to be completely lowered (translated). Standard translation only goes one level deep. 
            // Generic arguments are not translated until they are needed by metadata writer. 
            //
            // In order to get the fully lowered form we run the type symbols of stashed variables thru a deep translator
            // that translates the symbol recursively.
            _deepTranslator = new CSharpSymbolMatcher.DeepTranslator(sourceAssembly.GetSpecialType(SpecialType.System_Object));
        }
 public CompilationDifference(
     ImmutableArray<byte> metadata, 
     ImmutableArray<byte> il, 
     Stream pdbStream, 
     EmitBaseline nextGeneration,
     CompilationTestData testData,
     EmitResult result)
 {
     this.MetadataBlob = metadata;
     this.ILBlob = il;
     this.Pdb = pdbStream;
     this.NextGeneration = nextGeneration;
     this.TestData = testData;
     this.Result = result;
 }
        internal static CompilationDifference EmitDifference(
            this Compilation compilation,
            EmitBaseline baseline,
            ImmutableArray<SemanticEdit> edits,
            IEnumerable<ISymbol> allAddedSymbols = null,
            CompilationTestData testData = null)
        {
            testData = testData ?? new CompilationTestData();
            var isAddedSymbol = new Func<ISymbol, bool>(s => allAddedSymbols?.Contains(s) ?? false);

            var pdbName = Path.ChangeExtension(compilation.SourceModule.Name, "pdb");

            // keep the stream open, it's passed to CompilationDifference
            var pdbStream = new MemoryStream();

            using (MemoryStream mdStream = new MemoryStream(), ilStream = new MemoryStream())
            {
                var updatedMethods = new List<MethodDefinitionHandle>();

                var result = compilation.EmitDifference(
                    baseline,
                    edits,
                    isAddedSymbol,
                    mdStream,
                    ilStream,
                    pdbStream,
                    updatedMethods,
                    testData,
                    default(CancellationToken));

                pdbStream.Seek(0, SeekOrigin.Begin);

                return new CompilationDifference(
                    mdStream.ToImmutable(),
                    ilStream.ToImmutable(),
                    pdbStream,
                    testData,
                    result,
                    updatedMethods.ToImmutableArray());
            }
        }
        private bool TryGetMethodHandle(Microsoft.CodeAnalysis.Emit.EmitBaseline baseline, Cci.IMethodDefinition def, out MethodHandle handle)
        {
            if (this.TryGetMethodHandle(def, out handle))
            {
                return(true);
            }

            def = (Cci.IMethodDefinition) this.mapToPrevious.MapDefinition(def);
            if (def != null)
            {
                uint methodIndex;
                if (baseline.MethodsAdded.TryGetValue(def, out methodIndex))
                {
                    handle = MetadataTokens.MethodHandle((int)methodIndex);
                    return(true);
                }
            }

            handle = default(MethodHandle);
            return(false);
        }
Exemple #6
0
        private bool TryGetMethodHandle(EmitBaseline baseline, Cci.IMethodDefinition def, out MethodDefinitionHandle handle)
        {
            if (this.TryGetMethodHandle(def, out handle))
            {
                return(true);
            }

            def = (Cci.IMethodDefinition) this.MapToPreviousSymbolMatcher.MapDefinition(def);
            if (def != null)
            {
                int methodIndex;
                if (baseline.MethodsAdded.TryGetValue(def, out methodIndex))
                {
                    handle = MetadataTokens.MethodDefinitionHandle(methodIndex);
                    return(true);
                }
            }

            handle = default(MethodDefinitionHandle);
            return(false);
        }
Exemple #7
0
        public EmitBaseline MapBaselineToCompilation(
            EmitBaseline baseline,
            Compilation targetCompilation,
            CommonPEModuleBuilder targetModuleBuilder,
            ImmutableDictionary <ISymbolInternal, ImmutableArray <ISymbolInternal> > mappedSynthesizedMembers)
        {
            // Map all definitions to this compilation.
            var typesAdded         = MapDefinitions(baseline.TypesAdded);
            var eventsAdded        = MapDefinitions(baseline.EventsAdded);
            var fieldsAdded        = MapDefinitions(baseline.FieldsAdded);
            var methodsAdded       = MapDefinitions(baseline.MethodsAdded);
            var propertiesAdded    = MapDefinitions(baseline.PropertiesAdded);
            var generationOrdinals = MapDefinitions(baseline.GenerationOrdinals);

            return(baseline.With(
                       targetCompilation,
                       targetModuleBuilder,
                       baseline.Ordinal,
                       baseline.EncId,
                       generationOrdinals,
                       typesAdded,
                       eventsAdded,
                       fieldsAdded,
                       methodsAdded,
                       propertiesAdded,
                       eventMapAdded: baseline.EventMapAdded,
                       propertyMapAdded: baseline.PropertyMapAdded,
                       methodImplsAdded: baseline.MethodImplsAdded,
                       customAttributesAdded: baseline.CustomAttributesAdded,
                       tableEntriesAdded: baseline.TableEntriesAdded,
                       blobStreamLengthAdded: baseline.BlobStreamLengthAdded,
                       stringStreamLengthAdded: baseline.StringStreamLengthAdded,
                       userStringStreamLengthAdded: baseline.UserStringStreamLengthAdded,
                       guidStreamLengthAdded: baseline.GuidStreamLengthAdded,
                       anonymousTypeMap: MapAnonymousTypes(baseline.AnonymousTypeMap),
                       synthesizedMembers: mappedSynthesizedMembers,
                       addedOrChangedMethods: MapAddedOrChangedMethods(baseline.AddedOrChangedMethods),
                       debugInformationProvider: baseline.DebugInformationProvider,
                       localSignatureProvider: baseline.LocalSignatureProvider));
        }
        internal static CompilationDifference EmitDifference(
            this Compilation compilation,
            EmitBaseline baseline,
            ImmutableArray<SemanticEdit> edits,
            CompilationTestData testData = null)
        {
            testData = testData ?? new CompilationTestData();
            var pdbName = Path.ChangeExtension(compilation.SourceModule.Name, "pdb");

            // keep the stream open, it's passed to CompilationDifference
            var pdbStream = new MemoryStream();

            using (MemoryStream mdStream = new MemoryStream(), ilStream = new MemoryStream())
            {
                var updatedMethodTokens = new List<uint>();

                var result = compilation.EmitDifference(
                    baseline,
                    edits,
                    mdStream,
                    ilStream,
                    pdbStream,
                    updatedMethodTokens,
                    testData,
                    default(CancellationToken));

                pdbStream.Seek(0, SeekOrigin.Begin);

                return new CompilationDifference(
                    mdStream.ToImmutable(),
                    ilStream.ToImmutable(),
                    pdbStream,
                    result.Baseline,
                    testData,
                    result,
                    updatedMethodTokens.ToImmutableArray());
            }
        }
Exemple #9
0
        public EmitBaseline MapBaselineToCompilation(
            EmitBaseline baseline,
            Compilation targetCompilation,
            CommonPEModuleBuilder targetModuleBuilder,
            ImmutableDictionary <Cci.ITypeDefinition, ImmutableArray <Cci.ITypeDefinitionMember> > mappedSynthesizedMembers)
        {
            // Map all definitions to this compilation.
            IReadOnlyDictionary <Cci.ITypeDefinition, int>     typesAdded      = MapDefinitions(baseline.TypesAdded);
            IReadOnlyDictionary <Cci.IEventDefinition, int>    eventsAdded     = MapDefinitions(baseline.EventsAdded);
            IReadOnlyDictionary <Cci.IFieldDefinition, int>    fieldsAdded     = MapDefinitions(baseline.FieldsAdded);
            IReadOnlyDictionary <Cci.IMethodDefinition, int>   methodsAdded    = MapDefinitions(baseline.MethodsAdded);
            IReadOnlyDictionary <Cci.IPropertyDefinition, int> propertiesAdded = MapDefinitions(baseline.PropertiesAdded);

            return(baseline.With(
                       targetCompilation,
                       targetModuleBuilder,
                       baseline.Ordinal,
                       baseline.EncId,
                       typesAdded,
                       eventsAdded,
                       fieldsAdded,
                       methodsAdded,
                       propertiesAdded,
                       eventMapAdded: baseline.EventMapAdded,
                       propertyMapAdded: baseline.PropertyMapAdded,
                       methodImplsAdded: baseline.MethodImplsAdded,
                       tableEntriesAdded: baseline.TableEntriesAdded,
                       blobStreamLengthAdded: baseline.BlobStreamLengthAdded,
                       stringStreamLengthAdded: baseline.StringStreamLengthAdded,
                       userStringStreamLengthAdded: baseline.UserStringStreamLengthAdded,
                       guidStreamLengthAdded: baseline.GuidStreamLengthAdded,
                       anonymousTypeMap: this.MapAnonymousTypes(baseline.AnonymousTypeMap),
                       synthesizedMembers: mappedSynthesizedMembers,
                       addedOrChangedMethods: this.MapAddedOrChangedMethods(baseline.AddedOrChangedMethods),
                       debugInformationProvider: baseline.DebugInformationProvider));
        }
        public int EncApplySucceeded(int hrApplyResult)
        {
            try
            {
                log.Write("Change applied to {0}", _vsProject.DisplayName);
                Debug.Assert(IsDebuggable);
                Debug.Assert(_encService.EditSession != null);
                Debug.Assert(!_encService.EditSession.StoppedAtException);
                Debug.Assert(_pendingBaseline != null);

                // Since now on until exiting the break state, we consider the changes applied and the project state should be NoChanges.
                _changesApplied = true;

                _committedBaseline = _pendingBaseline;
                _pendingBaseline = null;

                return VSConstants.S_OK;
            }
            catch (Exception e) when (FatalError.ReportWithoutCrash(e))
            {
                return VSConstants.E_FAIL;
            }
        }
Exemple #11
0
        internal static EmitDifferenceResult EmitDifference(
            CSharpCompilation compilation,
            EmitBaseline baseline,
            IEnumerable<SemanticEdit> edits,
            Func<ISymbol, bool> isAddedSymbol,
            Stream metadataStream,
            Stream ilStream,
            Stream pdbStream,
            ICollection<MethodDefinitionHandle> updatedMethods,
            CompilationTestData testData,
            CancellationToken cancellationToken)
        {
            Guid moduleVersionId;
            try
            {
                moduleVersionId = baseline.OriginalMetadata.GetModuleVersionId();
            }
            catch (BadImageFormatException)
            {
                // TODO:
                // return MakeEmitResult(success: false, diagnostics: ..., baseline: null);
                throw;
            }

            var pdbName = FileNameUtilities.ChangeExtension(compilation.SourceModule.Name, "pdb");
            var diagnostics = DiagnosticBag.GetInstance();

            var emitOptions = EmitOptions.Default;
            string runtimeMDVersion = compilation.GetRuntimeMetadataVersion(emitOptions, diagnostics);
            var serializationProperties = compilation.ConstructModuleSerializationProperties(emitOptions, runtimeMDVersion, moduleVersionId);
            var manifestResources = SpecializedCollections.EmptyEnumerable<ResourceDescription>();

            var moduleBeingBuilt = new PEDeltaAssemblyBuilder(
                compilation.SourceAssembly,
                emitOptions: emitOptions,
                outputKind: compilation.Options.OutputKind,
                serializationProperties: serializationProperties,
                manifestResources: manifestResources,
                previousGeneration: baseline,
                edits: edits,
                isAddedSymbol: isAddedSymbol);

            if (testData != null)
            {
                moduleBeingBuilt.SetMethodTestData(testData.Methods);
                testData.Module = moduleBeingBuilt;
            }

            baseline = moduleBeingBuilt.PreviousGeneration;

            var definitionMap = moduleBeingBuilt.PreviousDefinitions;
            var changes = moduleBeingBuilt.Changes;

            if (compilation.Compile(
                moduleBeingBuilt,
                win32Resources: null,
                xmlDocStream: null,
                generateDebugInfo: true,
                diagnostics: diagnostics,
                filterOpt: changes.RequiresCompilation,
                cancellationToken: cancellationToken))
            {
                // 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.
                baseline = MapToCompilation(compilation, moduleBeingBuilt);

                using (var pdbWriter = new Cci.PdbWriter(pdbName, pdbStream, (testData != null) ? testData.SymWriterFactory : null))
                {
                    var context = new EmitContext(moduleBeingBuilt, null, diagnostics);
                    var encId = Guid.NewGuid();

                    try
                    {
                        var writer = new DeltaMetadataWriter(
                            context,
                            compilation.MessageProvider,
                            baseline,
                            encId,
                            definitionMap,
                            changes,
                            cancellationToken);

                        Cci.MetadataSizes metadataSizes;
                        writer.WriteMetadataAndIL(pdbWriter, metadataStream, ilStream, out metadataSizes);
                        writer.GetMethodTokens(updatedMethods);

                        bool hasErrors = diagnostics.HasAnyErrors();

                        return new EmitDifferenceResult(
                            success: !hasErrors,
                            diagnostics: diagnostics.ToReadOnlyAndFree(),
                            baseline: hasErrors ? null : writer.GetDelta(baseline, compilation, encId, metadataSizes));
                    }
                    catch (Cci.PdbWritingException e)
                    {
                        diagnostics.Add(ErrorCode.FTL_DebugEmitFailure, Location.None, e.Message);
                    }
                    catch (PermissionSetFileReadException e)
                    {
                        diagnostics.Add(ErrorCode.ERR_PermissionSetAttributeFileReadError, Location.None, e.FileName, e.PropertyName, e.Message);
                    }
                }
            }

            return new EmitDifferenceResult(success: false, diagnostics: diagnostics.ToReadOnlyAndFree(), baseline: null);
        }
        internal override bool TryGetPreviousLocals(
            Microsoft.CodeAnalysis.Emit.EmitBaseline baseline,
            IMethodSymbol method,
            out ImmutableArray <Microsoft.CodeAnalysis.Emit.EncLocalInfo> previousLocals,
            out GetPreviousLocalSlot getPreviousLocalSlot)
        {
            previousLocals       = default(ImmutableArray <Microsoft.CodeAnalysis.Emit.EncLocalInfo>);
            getPreviousLocalSlot = NoPreviousLocalSlot;

            MethodHandle handle;

            if (!this.TryGetMethodHandle(baseline, (Cci.IMethodDefinition)method, out handle))
            {
                // Unrecognized method. Must have been added in the current compilation.
                return(false);
            }

            MethodDefinitionEntry methodEntry;

            if (!this.methodMap.TryGetValue((MethodSymbol)method, out methodEntry))
            {
                // Not part of changeset. No need to preserve locals.
                return(false);
            }

            if (!methodEntry.PreserveLocalVariables)
            {
                // Not necessary to preserve locals.
                return(false);
            }

            var           previousMethod = (MethodSymbol)methodEntry.PreviousMethod;
            var           methodIndex    = (uint)MetadataTokens.GetRowNumber(handle);
            SymbolMatcher map;

            // Check if method has changed previously. If so, we already have a map.
            if (baseline.LocalsForMethodsAddedOrChanged.TryGetValue(methodIndex, out previousLocals))
            {
                map = this.mapToPrevious;
            }
            else
            {
                // Method has not changed since initial generation. Generate a map
                // using the local names provided with the initial metadata.
                var localNames = baseline.LocalNames(methodIndex);
                Debug.Assert(!localNames.IsDefault);

                var localInfo = default(ImmutableArray <MetadataDecoder.LocalInfo>);
                try
                {
                    Debug.Assert(this.module.HasIL);
                    var methodIL = this.module.GetMethodILOrThrow(handle);

                    if (!methodIL.LocalSignature.IsNil)
                    {
                        var signature = this.module.MetadataReader.GetLocalSignature(methodIL.LocalSignature);
                        localInfo = this.metadataDecoder.DecodeLocalSignatureOrThrow(signature);
                    }
                    else
                    {
                        localInfo = ImmutableArray <MetadataDecoder.LocalInfo> .Empty;
                    }
                }
                catch (UnsupportedSignatureContent)
                {
                }
                catch (BadImageFormatException)
                {
                }

                if (localInfo.IsDefault)
                {
                    // TODO: Report error that metadata is not supported.
                    return(false);
                }
                else
                {
                    // The signature may have more locals than names if trailing locals are unnamed.
                    // (Locals in the middle of the signature may be unnamed too but since localNames
                    // is indexed by slot, unnamed locals before the last named local will be represented
                    // as null values in the array.)
                    Debug.Assert(localInfo.Length >= localNames.Length);
                    previousLocals = GetLocalSlots(previousMethod, localNames, localInfo);
                    Debug.Assert(previousLocals.Length == localInfo.Length);
                }

                map = this.mapToMetadata;
            }

            // Find declarators in previous method syntax.
            // The locals are indices into this list.
            var previousDeclarators = GetLocalVariableDeclaratorsVisitor.GetDeclarators(previousMethod);

            // Create a map from declarator to declarator offset.
            var previousDeclaratorToOffset = new Dictionary <SyntaxNode, int>();

            for (int offset = 0; offset < previousDeclarators.Length; offset++)
            {
                previousDeclaratorToOffset.Add(previousDeclarators[offset], offset);
            }

            // Create a map from local info to slot.
            var previousLocalInfoToSlot = new Dictionary <Microsoft.CodeAnalysis.Emit.EncLocalInfo, int>();

            for (int slot = 0; slot < previousLocals.Length; slot++)
            {
                var localInfo = previousLocals[slot];
                Debug.Assert(!localInfo.IsDefault);
                if (localInfo.IsInvalid)
                {
                    // Unrecognized or deleted local.
                    continue;
                }
                previousLocalInfoToSlot.Add(localInfo, slot);
            }

            var syntaxMap = methodEntry.SyntaxMap;

            if (syntaxMap == null)
            {
                // If there was no syntax map, the syntax structure has not changed,
                // so we can map from current to previous syntax by declarator index.
                Debug.Assert(methodEntry.PreserveLocalVariables);
                // Create a map from declarator to declarator index.
                var currentDeclarators       = GetLocalVariableDeclaratorsVisitor.GetDeclarators((MethodSymbol)method);
                var currentDeclaratorToIndex = CreateDeclaratorToIndexMap(currentDeclarators);
                syntaxMap = currentSyntax =>
                {
                    var currentIndex = currentDeclaratorToIndex[(CSharpSyntaxNode)currentSyntax];
                    return(previousDeclarators[currentIndex]);
                };
            }

            getPreviousLocalSlot = (object identity, Cci.ITypeReference typeRef, LocalSlotConstraints constraints) =>
            {
                var local      = (LocalSymbol)identity;
                var syntaxRefs = local.DeclaringSyntaxReferences;
                Debug.Assert(!syntaxRefs.IsDefault);

                if (!syntaxRefs.IsDefaultOrEmpty)
                {
                    var currentSyntax  = syntaxRefs[0].GetSyntax();
                    var previousSyntax = (CSharpSyntaxNode)syntaxMap(currentSyntax);
                    if (previousSyntax != null)
                    {
                        int offset;
                        if (previousDeclaratorToOffset.TryGetValue(previousSyntax, out offset))
                        {
                            var previousType = map.MapReference(typeRef);
                            if (previousType != null)
                            {
                                var localKey = new Microsoft.CodeAnalysis.Emit.EncLocalInfo(offset, previousType, constraints, (int)local.TempKind);
                                int slot;
                                // Should report a warning if the type of the local has changed
                                // and the previous value will be dropped. (Bug #781309.)
                                if (previousLocalInfoToSlot.TryGetValue(localKey, out slot))
                                {
                                    return(slot);
                                }
                            }
                        }
                    }
                }

                return(-1);
            };
            return(true);
        }
Exemple #13
0
        internal override bool TryGetPreviousLocals(
            EmitBaseline baseline,
            IMethodSymbol method,
            out ImmutableArray<EncLocalInfo> previousLocals,
            out GetPreviousLocalSlot getPreviousLocalSlot)
        {
            previousLocals = default(ImmutableArray<EncLocalInfo>);
            getPreviousLocalSlot = NoPreviousLocalSlot;

            MethodHandle handle;
            if (!this.TryGetMethodHandle(baseline, (Cci.IMethodDefinition)method, out handle))
            {
                // Unrecognized method. Must have been added in the current compilation.
                return false;
            }

            MethodDefinitionEntry methodEntry;
            if (!this.methodMap.TryGetValue(method, out methodEntry))
            {
                // Not part of changeset. No need to preserve locals.
                return false;
            }

            if (!methodEntry.PreserveLocalVariables)
            {
                // Not necessary to preserve locals.
                return false;
            }

            var previousMethod = methodEntry.PreviousMethod;
            var methodIndex = (uint)MetadataTokens.GetRowNumber(handle);
            SymbolMatcher map;

            // Check if method has changed previously. If so, we already have a map.
            if (baseline.LocalsForMethodsAddedOrChanged.TryGetValue(methodIndex, out previousLocals))
            {
                map = this.mapToPrevious;
            }
            else
            {
                // Method has not changed since initial generation. Generate a map
                // using the local names provided with the initial metadata.
                var localNames = baseline.LocalNames(methodIndex);
                Debug.Assert(!localNames.IsDefault);

                var localInfo = default(ImmutableArray<MetadataDecoder.LocalInfo>);
                try
                {
                    Debug.Assert(this.module.HasIL);
                    var methodBody = this.module.GetMethodBodyOrThrow(handle);

                    if (!methodBody.LocalSignature.IsNil)
                    {
                        var signature = this.module.MetadataReader.GetLocalSignature(methodBody.LocalSignature);
                        localInfo = this.metadataDecoder.DecodeLocalSignatureOrThrow(signature);
                    }
                    else
                    {
                        localInfo = ImmutableArray<MetadataDecoder.LocalInfo>.Empty;
                    }
                }
                catch (UnsupportedSignatureContent)
                {
                }
                catch (BadImageFormatException)
                {
                }

                if (localInfo.IsDefault)
                {
                    // TODO: Report error that metadata is not supported.
                    return false;
                }
                else
                {
                    // The signature may have more locals than names if trailing locals are unnamed.
                    // (Locals in the middle of the signature may be unnamed too but since localNames
                    // is indexed by slot, unnamed locals before the last named local will be represented
                    // as null values in the array.)
                    Debug.Assert(localInfo.Length >= localNames.Length);
                    previousLocals = GetLocalSlots(previousMethod, localNames, localInfo);
                    Debug.Assert(previousLocals.Length == localInfo.Length);
                }

                map = this.mapToMetadata;
            }

            // Find declarators in previous method syntax.
            // The locals are indices into this list.
            var previousDeclarators = LocalVariableDeclaratorsCollector.GetDeclarators(previousMethod);

            // Create a map from declarator to declarator offset.
            var previousDeclaratorToOffset = new Dictionary<SyntaxNode, int>();
            for (int offset = 0; offset < previousDeclarators.Length; offset++)
            {
                previousDeclaratorToOffset.Add(previousDeclarators[offset], offset);
            }

            // Create a map from local info to slot.
            var previousLocalInfoToSlot = new Dictionary<EncLocalInfo, int>();
            for (int slot = 0; slot < previousLocals.Length; slot++)
            {
                var localInfo = previousLocals[slot];
                Debug.Assert(!localInfo.IsDefault);
                if (localInfo.IsInvalid)
                {
                    // Unrecognized or deleted local.
                    continue;
                }
                previousLocalInfoToSlot.Add(localInfo, slot);
            }

            var syntaxMap = methodEntry.SyntaxMap;
            if (syntaxMap == null)
            {
                // If there was no syntax map, the syntax structure has not changed,
                // so we can map from current to previous syntax by declarator index.
                Debug.Assert(methodEntry.PreserveLocalVariables);
                // Create a map from declarator to declarator index.
                var currentDeclarators = LocalVariableDeclaratorsCollector.GetDeclarators(method);
                var currentDeclaratorToIndex = CreateDeclaratorToIndexMap(currentDeclarators);
                syntaxMap = currentSyntax =>
                {
                    var currentIndex = currentDeclaratorToIndex[(CSharpSyntaxNode)currentSyntax];
                    return previousDeclarators[currentIndex];
                };
            }

            getPreviousLocalSlot = (object identity, Cci.ITypeReference typeRef, LocalSlotConstraints constraints) =>
            {
                var local = (LocalSymbol)identity;
                var syntaxRefs = local.DeclaringSyntaxReferences;
                Debug.Assert(!syntaxRefs.IsDefault);

                if (!syntaxRefs.IsDefaultOrEmpty)
                {
                    var currentSyntax = syntaxRefs[0].GetSyntax();
                    var previousSyntax = (CSharpSyntaxNode)syntaxMap(currentSyntax);
                    if (previousSyntax != null)
                    {
                        int offset;
                        if (previousDeclaratorToOffset.TryGetValue(previousSyntax, out offset))
                        {
                            var previousType = map.MapReference(typeRef);
                            if (previousType != null)
                            {
                                var localKey = new EncLocalInfo(offset, previousType, constraints, (int)local.TempKind);
                                int slot;
                                // Should report a warning if the type of the local has changed
                                // and the previous value will be dropped. (Bug #781309.)
                                if (previousLocalInfoToSlot.TryGetValue(localKey, out slot))
                                {
                                    return slot;
                                }
                            }
                        }
                    }
                }

                return -1;
            };

            return true;
        }
Exemple #14
0
        internal VariableSlotAllocator?TryCreateVariableSlotAllocator(EmitBaseline baseline, Compilation compilation, IMethodSymbolInternal method, IMethodSymbolInternal topLevelMethod, DiagnosticBag diagnostics)
        {
            // Top-level methods are always included in the semantic edit list. Lambda methods are not.
            if (!mappedMethods.TryGetValue(topLevelMethod, out var mappedMethod))
            {
                return(null);
            }

            // TODO (bug https://github.com/dotnet/roslyn/issues/2504):
            // Handle cases when the previous method doesn't exist.

            if (!TryGetMethodHandle(baseline, (Cci.IMethodDefinition)method.GetCciAdapter(), out var previousHandle))
            {
                // Unrecognized method. Must have been added in the current compilation.
                return(null);
            }

            ImmutableArray <EncLocalInfo> previousLocals;
            IReadOnlyDictionary <EncHoistedLocalInfo, int>?         hoistedLocalMap = null;
            IReadOnlyDictionary <Cci.ITypeReference, int>?          awaiterMap      = null;
            IReadOnlyDictionary <int, KeyValuePair <DebugId, int> >?lambdaMap       = null;
            IReadOnlyDictionary <int, DebugId>?closureMap           = null;
            IReadOnlyDictionary <int, int>?    stateMachineStateMap = null;
            int?firstUnusedIncreasingStateMachineState = null;
            int?firstUnusedDecreasingStateMachineState = null;

            int           hoistedLocalSlotCount = 0;
            int           awaiterSlotCount      = 0;
            string?       stateMachineTypeName  = null;
            SymbolMatcher symbolMap;

            int     methodIndex = MetadataTokens.GetRowNumber(previousHandle);
            DebugId methodId;

            // Check if method has changed previously. If so, we already have a map.
            if (baseline.AddedOrChangedMethods.TryGetValue(methodIndex, out var addedOrChangedMethod))
            {
                methodId = addedOrChangedMethod.MethodId;

                MakeLambdaAndClosureMaps(addedOrChangedMethod.LambdaDebugInfo, addedOrChangedMethod.ClosureDebugInfo, out lambdaMap, out closureMap);
                MakeStateMachineStateMap(addedOrChangedMethod.StateMachineStates.States, out stateMachineStateMap);

                firstUnusedIncreasingStateMachineState = addedOrChangedMethod.StateMachineStates.FirstUnusedIncreasingStateMachineState;
                firstUnusedDecreasingStateMachineState = addedOrChangedMethod.StateMachineStates.FirstUnusedDecreasingStateMachineState;

                if (addedOrChangedMethod.StateMachineTypeName != null)
                {
                    // method is async/iterator kickoff method
                    GetStateMachineFieldMapFromPreviousCompilation(
                        addedOrChangedMethod.StateMachineHoistedLocalSlotsOpt,
                        addedOrChangedMethod.StateMachineAwaiterSlotsOpt,
                        out hoistedLocalMap,
                        out awaiterMap);

                    hoistedLocalSlotCount = addedOrChangedMethod.StateMachineHoistedLocalSlotsOpt.Length;
                    awaiterSlotCount      = addedOrChangedMethod.StateMachineAwaiterSlotsOpt.Length;

                    // Kickoff method has no interesting locals on its own.
                    // We use the EnC method debug information for hoisted locals.
                    previousLocals = ImmutableArray <EncLocalInfo> .Empty;

                    stateMachineTypeName = addedOrChangedMethod.StateMachineTypeName;
                }
                else
                {
                    previousLocals = addedOrChangedMethod.Locals;
                }

                // All types that AddedOrChangedMethodInfo refers to have been mapped to the previous generation.
                // Therefore we don't need to fall back to metadata if we don't find the type reference, like we do in DefinitionMap.MapReference.
                symbolMap = MapToPreviousSymbolMatcher;
            }
            else
            {
                // Method has not changed since initial generation. Generate a map
                // using the local names provided with the initial metadata.
                EditAndContinueMethodDebugInformation debugInfo;
                StandaloneSignatureHandle             localSignature;
                try
                {
                    debugInfo      = baseline.DebugInformationProvider(previousHandle);
                    localSignature = baseline.LocalSignatureProvider(previousHandle);
                }
                catch (Exception e) when(e is InvalidDataException || e is IOException)
                {
                    diagnostics.Add(MessageProvider.CreateDiagnostic(
                                        MessageProvider.ERR_InvalidDebugInfo,
                                        method.Locations.First(),
                                        method,
                                        MetadataTokens.GetToken(previousHandle),
                                        method.ContainingAssembly
                                        ));

                    return(null);
                }

                methodId = new DebugId(debugInfo.MethodOrdinal, 0);

                if (!debugInfo.Lambdas.IsDefaultOrEmpty)
                {
                    MakeLambdaAndClosureMaps(debugInfo.Lambdas, debugInfo.Closures, out lambdaMap, out closureMap);
                }

                MakeStateMachineStateMap(debugInfo.StateMachineStates, out stateMachineStateMap);

                if (!debugInfo.StateMachineStates.IsDefaultOrEmpty)
                {
                    firstUnusedIncreasingStateMachineState = debugInfo.StateMachineStates.Max(s => s.StateNumber) + 1;
                    firstUnusedDecreasingStateMachineState = debugInfo.StateMachineStates.Min(s => s.StateNumber) - 1;
                }

                ITypeSymbolInternal?stateMachineType = TryGetStateMachineType(previousHandle);
                if (stateMachineType != null)
                {
                    // method is async/iterator kickoff method
                    var localSlotDebugInfo = debugInfo.LocalSlots.NullToEmpty();
                    GetStateMachineFieldMapFromMetadata(stateMachineType, localSlotDebugInfo, out hoistedLocalMap, out awaiterMap, out awaiterSlotCount);
                    hoistedLocalSlotCount = localSlotDebugInfo.Length;

                    // Kickoff method has no interesting locals on its own.
                    // We use the EnC method debug information for hoisted locals.
                    previousLocals = ImmutableArray <EncLocalInfo> .Empty;

                    stateMachineTypeName = stateMachineType.Name;
                }
                else
                {
                    // If the current method is async/iterator then either the previous method wasn't declared as async/iterator and it's updated to be one,
                    // or it was but is not marked by the corresponding state machine attribute because it was missing in the compilation.
                    // In the later case we need to report an error since we don't known how to map to the previous state machine.

                    // The IDE already checked that the attribute type is present in the base compilation, but didn't validate that it is well-formed.
                    // We don't have the base compilation to directly query for the attribute, only the source compilation.
                    // But since constructor signatures can't be updated during EnC we can just check the current compilation.

                    if (method.IsAsync)
                    {
                        if (compilation.CommonGetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_AsyncStateMachineAttribute__ctor) == null)
                        {
                            ReportMissingStateMachineAttribute(diagnostics, method, AttributeDescription.AsyncStateMachineAttribute.FullName);
                            return(null);
                        }
                    }
                    else if (method.IsIterator)
                    {
                        if (compilation.CommonGetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_IteratorStateMachineAttribute__ctor) == null)
                        {
                            ReportMissingStateMachineAttribute(diagnostics, method, AttributeDescription.IteratorStateMachineAttribute.FullName);
                            return(null);
                        }
                    }

                    try
                    {
                        previousLocals = localSignature.IsNil ? ImmutableArray <EncLocalInfo> .Empty :
                                         GetLocalSlotMapFromMetadata(localSignature, debugInfo);
                    }
                    catch (Exception e) when(e is UnsupportedSignatureContent || e is BadImageFormatException || e is IOException)
                    {
                        diagnostics.Add(MessageProvider.CreateDiagnostic(
                                            MessageProvider.ERR_InvalidDebugInfo,
                                            method.Locations.First(),
                                            method,
                                            MetadataTokens.GetToken(localSignature),
                                            method.ContainingAssembly
                                            ));

                        return(null);
                    }
                }

                symbolMap = MapToMetadataSymbolMatcher;
            }

            return(new EncVariableSlotAllocator(
                       symbolMap,
                       mappedMethod.SyntaxMap,
                       mappedMethod.PreviousMethod,
                       methodId,
                       previousLocals,
                       lambdaMap,
                       closureMap,
                       stateMachineTypeName,
                       hoistedLocalSlotCount,
                       hoistedLocalMap,
                       awaiterSlotCount,
                       awaiterMap,
                       stateMachineStateMap,
                       firstUnusedIncreasingStateMachineState,
                       firstUnusedDecreasingStateMachineState,
                       GetLambdaSyntaxFacts()));
        }
        private static EmitBaseline EnsureInitialized(
            EmitBaseline previousGeneration,
            Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE.MetadataDecoder metadataDecoder)
        {
            if (previousGeneration.AnonymousTypeMap != null)
            {
                return previousGeneration;
            }

            var anonymousTypeMap = GetAnonymousTypeMapFromMetadata(previousGeneration.MetadataReader, metadataDecoder);
            return previousGeneration.WithAnonymousTypeMap(anonymousTypeMap);
        }
Exemple #16
0
 internal abstract VariableSlotAllocator TryCreateVariableSlotAllocator(EmitBaseline baseline, IMethodSymbol method);
Exemple #17
0
 internal abstract bool TryGetPreviousLocals(
     EmitBaseline baseline,
     IMethodSymbol method,
     out ImmutableArray<EncLocalInfo> previousLocals,
     out GetPreviousLocalSlot getPreviousLocalSlot);
Exemple #18
0
 internal EmitResult(bool success, ImmutableArray<Diagnostic> diagnostics, EmitBaseline baseline)
     : base(success, baseline)
 {
     this.diagnostics = diagnostics;
 }
        public int StopDebuggingPE()
        {
            try
            {
                log.Write("Exit Debug Mode: project '{0}'", _vsProject.DisplayName);
                Debug.Assert(s_breakStateEnteredProjects.Count == 0);

                // EnC service is global (per solution), but the debugger calls this for each project.
                // Avoid ending the debug session if it has already been ended.
                if (_encService.DebuggingSession != null)
                {
                    _encService.EndDebuggingSession();
                    LogEncSession();

                    s_encDebuggingSessionInfo = null;
                    s_readOnlyDocumentTracker.Dispose();
                    s_readOnlyDocumentTracker = null;
                }

                if (_metadata != null)
                {
                    _metadata.Dispose();
                    _metadata = null;

                    s_debugStateProjectCount--;
                }
                else
                {
                    // an error might have been reported:
                    _diagnosticProvider.ClearDiagnostics(_encService.DebuggingSession, _vsProject.VisualStudioWorkspace, EditAndContinueDiagnosticUpdateSource.DebuggerErrorId, _vsProject.Id, documentId: null);
                }

                _activeMethods = null;
                _exceptionRegions = null;
                _committedBaseline = null;
                _activeStatementIds = null;

                if (_pdbReaderObj != null)
                {
                    Marshal.ReleaseComObject(_pdbReaderObj);
                    _pdbReaderObj = null;
                }

                if (_pdbProvider != null)
                {
                    _pdbProvider.Dispose();
                    _pdbProvider = null;
                }

                _pdbReader = null;

                // The HResult is ignored by the debugger.
                return VSConstants.S_OK;
            }
            catch (Exception e) when(FatalError.Report(e))
            {
                throw ExceptionUtilities.Unreachable;
            }
            }
        public int StopDebuggingPE()
        {
            try
            {
                log.Write("Exit Debug Mode: project '{0}'", _vsProject.DisplayName);
                Debug.Assert(s_breakStateEnteredProjects.Count == 0);

                // Clear the solution stored while projects were entering break mode. 
                // It should be cleared as soon as all tracked projects enter the break mode 
                // but if the entering break mode fails for some projects we should avoid leaking the solution.
                Debug.Assert(s_breakStateEntrySolution == null);
                s_breakStateEntrySolution = null;

                // EnC service is global (per solution), but the debugger calls this for each project.
                // Avoid ending the debug session if it has already been ended.
                if (_encService.DebuggingSession != null)
                {
                    _encService.OnBeforeDebuggingStateChanged(DebuggingState.Run, DebuggingState.Design);

                    _encService.EndDebuggingSession();
                    LogEncSession();

                    s_encDebuggingSessionInfo = null;
                    s_readOnlyDocumentTracker.Dispose();
                    s_readOnlyDocumentTracker = null;
                }

                if (_metadata != null)
                {
                    _metadata.Dispose();
                    _metadata = null;

                    s_debugStateProjectCount--;
                }
                else
                {
                    // an error might have been reported:
                    _diagnosticProvider.ClearDiagnostics(_encService.DebuggingSession, _vsProject.VisualStudioWorkspace, EditAndContinueDiagnosticUpdateSource.DebuggerErrorId, _vsProject.Id, documentId: null);
                }

                _activeMethods = null;
                _exceptionRegions = null;
                _committedBaseline = null;
                _activeStatementIds = null;

                Debug.Assert((_pdbReaderObjAsStream == IntPtr.Zero) || (_pdbReader == null));

                if (_pdbReader != null)
                {
                    Marshal.ReleaseComObject(_pdbReader);
                    _pdbReader = null;
                }

                // The HResult is ignored by the debugger.
                return VSConstants.S_OK;
            }
            catch (Exception e) when (FatalError.ReportWithoutCrash(e))
            {
                return VSConstants.E_FAIL;
            }
        }
        public unsafe int BuildForEnc(object pUpdatePE)
        {
            try
            {
                log.Write("Applying changes to {0}", _vsProject.DisplayName);

                Debug.Assert(_encService.EditSession != null);
                Debug.Assert(!_encService.EditSession.StoppedAtException);

                // Non-debuggable project has no changes.
                Debug.Assert(IsDebuggable);

                if (_changesApplied)
                {
                    log.Write("Changes already applied to {0}, can't apply again", _vsProject.DisplayName);
                    throw ExceptionUtilities.Unreachable;
                }

                // The debugger always calls GetENCBuildState right before BuildForEnc.
                Debug.Assert(_projectBeingEmitted != null);
                Debug.Assert(_lastEditSessionSummary == GetProjectAnalysisSummary(_projectBeingEmitted));

                // The debugger should have called GetENCBuildState before calling BuildForEnc.
                // Unfortunately, there is no way how to tell the debugger that the changes were not significant,
                // so we'll to emit an empty delta. See bug 839558.
                Debug.Assert(_lastEditSessionSummary == ProjectAnalysisSummary.ValidInsignificantChanges ||
                             _lastEditSessionSummary == ProjectAnalysisSummary.ValidChanges);

                var updater = (IDebugUpdateInMemoryPE2)pUpdatePE;
                if (_committedBaseline == null)
                {
                    var hr = MarshalPdbReader(updater, out _pdbReaderObjAsStream);
                    if (hr != VSConstants.S_OK)
                    {
                        return hr;
                    }

                    _committedBaseline = EmitBaseline.CreateInitialBaseline(_metadata, GetBaselineEncDebugInfo);
                }

                // ISymUnmanagedReader can only be accessed from an MTA thread,
                // so dispatch it to one of thread pool threads, which are MTA.
                var emitTask = Task.Factory.SafeStartNew(EmitProjectDelta, CancellationToken.None, TaskScheduler.Default);

                Deltas delta;
                using (NonReentrantContext)
                {
                    delta = emitTask.Result;
                }

                // Clear diagnostics, in case the project was built before and failed due to errors.
                _diagnosticProvider.ClearDiagnostics(_encService.DebuggingSession, _vsProject.VisualStudioWorkspace, EditAndContinueDiagnosticUpdateSource.EmitErrorId, _vsProject.Id, _documentsWithEmitError);

                if (!delta.EmitResult.Success)
                {
                    var errors = delta.EmitResult.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error);
                    _documentsWithEmitError = _diagnosticProvider.ReportDiagnostics(
                        _encService.DebuggingSession,
                        EditAndContinueDiagnosticUpdateSource.EmitErrorId,
                        _vsProject.Id,
                        _projectBeingEmitted.Solution,
                        errors);

                    _encService.EditSession.LogEmitProjectDeltaErrors(errors.Select(e => e.Id));

                    return VSConstants.E_FAIL;
                }

                _documentsWithEmitError = default(ImmutableArray<DocumentId>);
                SetFileUpdates(updater, delta.LineEdits);

                updater.SetDeltaIL(delta.IL.Value, (uint)delta.IL.Value.Length);
                updater.SetDeltaPdb(new ComStreamWrapper(delta.Pdb.Stream));
                updater.SetRemapMethods(delta.Pdb.UpdatedMethods, (uint)delta.Pdb.UpdatedMethods.Length);
                updater.SetDeltaMetadata(delta.Metadata.Bytes, (uint)delta.Metadata.Bytes.Length);

                _pendingBaseline = delta.EmitResult.Baseline;

#if DEBUG
                fixed (byte* deltaMetadataPtr = &delta.Metadata.Bytes[0])
                {
                    var reader = new System.Reflection.Metadata.MetadataReader(deltaMetadataPtr, delta.Metadata.Bytes.Length);
                    var moduleDef = reader.GetModuleDefinition();

                    log.DebugWrite("Gen {0}: MVID={1}, BaseId={2}, EncId={3}",
                        moduleDef.Generation,
                        reader.GetGuid(moduleDef.Mvid),
                        reader.GetGuid(moduleDef.BaseGenerationId),
                        reader.GetGuid(moduleDef.GenerationId));
                }
#endif

                return VSConstants.S_OK;
            }
            catch (Exception e) when (FatalError.ReportWithoutCrash(e))
            {
                return VSConstants.E_FAIL;
            }
        }
        private static EmitBaseline EnsureInitialized(
            EmitBaseline previousGeneration,
            Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE.MetadataDecoder metadataDecoder)
        {
            if (previousGeneration.AnonymousTypeMap != null)
            {
                return previousGeneration;
            }

            var anonymousTypeMap = GetAnonymousTypeMap(previousGeneration.MetadataReader, metadataDecoder);
            return previousGeneration.With(
                previousGeneration.Compilation,
                previousGeneration.PEModuleBuilder,
                previousGeneration.Ordinal,
                previousGeneration.EncId,
                previousGeneration.TypesAdded,
                previousGeneration.EventsAdded,
                previousGeneration.FieldsAdded,
                previousGeneration.MethodsAdded,
                previousGeneration.PropertiesAdded,
                previousGeneration.EventMapAdded,
                previousGeneration.PropertyMapAdded,
                previousGeneration.TableEntriesAdded,
                blobStreamLengthAdded: previousGeneration.BlobStreamLengthAdded,
                stringStreamLengthAdded: previousGeneration.StringStreamLengthAdded,
                userStringStreamLengthAdded: previousGeneration.UserStringStreamLengthAdded,
                guidStreamLengthAdded: previousGeneration.GuidStreamLengthAdded,
                anonymousTypeMap: anonymousTypeMap,
                localsForMethodsAddedOrChanged: previousGeneration.LocalsForMethodsAddedOrChanged,
                localNames: previousGeneration.LocalNames);
        }
Exemple #23
0
 internal abstract bool TryGetPreviousLocals(
     EmitBaseline baseline,
     IMethodSymbol method,
     out ImmutableArray <EncLocalInfo> previousLocals,
     out GetPreviousLocalSlot getPreviousLocalSlot);
Exemple #24
0
        public async Task<Deltas> EmitProjectDeltaAsync(Project project, EmitBaseline baseline, CancellationToken cancellationToken)
        {
            try
            {
                Debug.Assert(!_stoppedAtException);

                var changes = await GetProjectChangesAsync(project, cancellationToken).ConfigureAwait(false);
                var currentCompilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
                var allAddedSymbols = await GetAllAddedSymbols(cancellationToken).ConfigureAwait(false);

                var pdbStream = new MemoryStream();
                var updatedMethods = new List<MethodDefinitionHandle>();

                using (var metadataStream = SerializableBytes.CreateWritableStream())
                using (var ilStream = SerializableBytes.CreateWritableStream())
                {
                    EmitDifferenceResult result = currentCompilation.EmitDifference(
                        baseline,
                        changes.SemanticEdits,
                        s => allAddedSymbols?.Contains(s) ?? false,
                        metadataStream,
                        ilStream,
                        pdbStream,
                        updatedMethods,
                        cancellationToken);

                    int[] updateMethodTokens = updatedMethods.Select(h => MetadataTokens.GetToken(h)).ToArray();
                    return new Deltas(ilStream.ToArray(), metadataStream.ToArray(), updateMethodTokens, pdbStream, changes.LineChanges, result);
                }
            }
            catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
 internal EmitDifferenceResult(bool success, ImmutableArray<Diagnostic> diagnostics, EmitBaseline baseline) :
     base(success, diagnostics)
 {
     _baseline = baseline;
 }
Exemple #26
0
        private EmitBaseline(
            EmitBaseline initialBaseline,
            ModuleMetadata module,
            Compilation compilation,
            CommonPEModuleBuilder moduleBuilder,
            Guid moduleVersionId,
            int ordinal,
            Guid encId,
            IReadOnlyDictionary <Cci.ITypeDefinition, uint> typesAdded,
            IReadOnlyDictionary <Cci.IEventDefinition, uint> eventsAdded,
            IReadOnlyDictionary <Cci.IFieldDefinition, uint> fieldsAdded,
            IReadOnlyDictionary <Cci.IMethodDefinition, uint> methodsAdded,
            IReadOnlyDictionary <Cci.IPropertyDefinition, uint> propertiesAdded,
            IReadOnlyDictionary <uint, uint> eventMapAdded,
            IReadOnlyDictionary <uint, uint> propertyMapAdded,
            IReadOnlyDictionary <MethodImplKey, uint> methodImplsAdded,
            ImmutableArray <int> tableEntriesAdded,
            int blobStreamLengthAdded,
            int stringStreamLengthAdded,
            int userStringStreamLengthAdded,
            int guidStreamLengthAdded,
            IReadOnlyDictionary <AnonymousTypeKey, AnonymousTypeValue> anonymousTypeMap,
            ImmutableDictionary <Cci.ITypeDefinition, ImmutableArray <Cci.ITypeDefinitionMember> > synthesizedMembers,
            IReadOnlyDictionary <uint, AddedOrChangedMethodInfo> methodsAddedOrChanged,
            Func <MethodDefinitionHandle, EditAndContinueMethodDebugInformation> debugInformationProvider,
            IReadOnlyDictionary <uint, uint> typeToEventMap,
            IReadOnlyDictionary <uint, uint> typeToPropertyMap,
            IReadOnlyDictionary <MethodImplKey, uint> methodImpls)
        {
            Debug.Assert(module != null);
            Debug.Assert((ordinal == 0) == (encId == default(Guid)));
            Debug.Assert((ordinal == 0) == (initialBaseline == null));
            Debug.Assert(encId != module.GetModuleVersionId());
            Debug.Assert(debugInformationProvider != null);
            Debug.Assert(typeToEventMap != null);
            Debug.Assert(typeToPropertyMap != null);
            Debug.Assert(moduleVersionId != default(Guid));
            Debug.Assert(moduleVersionId == module.GetModuleVersionId());
            Debug.Assert(synthesizedMembers != null);

            Debug.Assert(tableEntriesAdded.Length == MetadataTokens.TableCount);

            // The size of each table is the total number of entries added in all previous
            // generations after the initial generation. Depending on the table, some of the
            // entries may not be available in the current generation (say, a synthesized type
            // from a method that was not recompiled for instance)
            Debug.Assert(tableEntriesAdded[(int)TableIndex.TypeDef] >= typesAdded.Count);
            Debug.Assert(tableEntriesAdded[(int)TableIndex.Event] >= eventsAdded.Count);
            Debug.Assert(tableEntriesAdded[(int)TableIndex.Field] >= fieldsAdded.Count);
            Debug.Assert(tableEntriesAdded[(int)TableIndex.MethodDef] >= methodsAdded.Count);
            Debug.Assert(tableEntriesAdded[(int)TableIndex.Property] >= propertiesAdded.Count);
            Debug.Assert(tableEntriesAdded[(int)TableIndex.EventMap] >= eventMapAdded.Count);
            Debug.Assert(tableEntriesAdded[(int)TableIndex.PropertyMap] >= propertyMapAdded.Count);

            var reader = module.Module.MetadataReader;

            this.InitialBaseline  = initialBaseline ?? this;
            this.OriginalMetadata = module;
            this.Compilation      = compilation;
            this.PEModuleBuilder  = moduleBuilder;
            this.ModuleVersionId  = moduleVersionId;
            this.Ordinal          = ordinal;
            this.EncId            = encId;

            this.TypesAdded                  = typesAdded;
            this.EventsAdded                 = eventsAdded;
            this.FieldsAdded                 = fieldsAdded;
            this.MethodsAdded                = methodsAdded;
            this.PropertiesAdded             = propertiesAdded;
            this.EventMapAdded               = eventMapAdded;
            this.PropertyMapAdded            = propertyMapAdded;
            this.MethodImplsAdded            = methodImplsAdded;
            this.TableEntriesAdded           = tableEntriesAdded;
            this.BlobStreamLengthAdded       = blobStreamLengthAdded;
            this.StringStreamLengthAdded     = stringStreamLengthAdded;
            this.UserStringStreamLengthAdded = userStringStreamLengthAdded;
            this.GuidStreamLengthAdded       = guidStreamLengthAdded;
            this._anonymousTypeMap           = anonymousTypeMap;
            this.SynthesizedMembers          = synthesizedMembers;
            this.AddedOrChangedMethods       = methodsAddedOrChanged;

            this.DebugInformationProvider = debugInformationProvider;
            this.TableSizes        = CalculateTableSizes(reader, this.TableEntriesAdded);
            this.TypeToEventMap    = typeToEventMap;
            this.TypeToPropertyMap = typeToPropertyMap;
            this.MethodImpls       = methodImpls;
        }
Exemple #27
0
        internal static EmitDifferenceResult EmitDifference(
            CSharpCompilation compilation,
            EmitBaseline baseline,
            IEnumerable<SemanticEdit> edits,
            Func<ISymbol, bool> isAddedSymbol,
            Stream metadataStream,
            Stream ilStream,
            Stream pdbStream,
            ICollection<MethodDefinitionHandle> updatedMethods,
            CompilationTestData testData,
            CancellationToken cancellationToken)
        {
            Guid moduleVersionId;
            try
            {
                moduleVersionId = baseline.OriginalMetadata.GetModuleVersionId();
            }
            catch (BadImageFormatException)
            {
                // TODO:
                // return MakeEmitResult(success: false, diagnostics: ..., baseline: null);
                throw;
            }

            var diagnostics = DiagnosticBag.GetInstance();

            var emitOptions = EmitOptions.Default;
            string runtimeMDVersion = compilation.GetRuntimeMetadataVersion(emitOptions, diagnostics);
            var serializationProperties = compilation.ConstructModuleSerializationProperties(emitOptions, runtimeMDVersion, moduleVersionId);
            var manifestResources = SpecializedCollections.EmptyEnumerable<ResourceDescription>();

            var moduleBeingBuilt = new PEDeltaAssemblyBuilder(
                compilation.SourceAssembly,
                emitOptions: emitOptions,
                outputKind: compilation.Options.OutputKind,
                serializationProperties: serializationProperties,
                manifestResources: manifestResources,
                previousGeneration: baseline,
                edits: edits,
                isAddedSymbol: isAddedSymbol);

            if (testData != null)
            {
                moduleBeingBuilt.SetMethodTestData(testData.Methods);
                testData.Module = moduleBeingBuilt;
            }

            var definitionMap = moduleBeingBuilt.PreviousDefinitions;
            var changes = moduleBeingBuilt.Changes;

            EmitBaseline newBaseline = null;

            if (compilation.Compile(
                moduleBeingBuilt,
                win32Resources: null,
                xmlDocStream: null,
                emittingPdb: true,
                diagnostics: diagnostics,
                filterOpt: changes.RequiresCompilation,
                cancellationToken: cancellationToken))
            {
                // 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.
                var mappedBaseline = MapToCompilation(compilation, moduleBeingBuilt);

                newBaseline = compilation.SerializeToDeltaStreams(
                    moduleBeingBuilt,
                    mappedBaseline, 
                    definitionMap,
                    changes,
                    metadataStream,
                    ilStream,
                    pdbStream,
                    updatedMethods,
                    diagnostics,
                    testData?.SymWriterFactory,
                    cancellationToken);
            }

            return new EmitDifferenceResult(
                success: newBaseline != null,
                diagnostics: diagnostics.ToReadOnlyAndFree(),
                baseline: newBaseline);
        }
 internal EmitDifferenceResult(bool success, ImmutableArray <Diagnostic> diagnostics, EmitBaseline baseline) :
     base(success, diagnostics)
 {
     _baseline = baseline;
 }
Exemple #29
0
        private bool TryGetMethodHandle(EmitBaseline baseline, Cci.IMethodDefinition def, out MethodHandle handle)
        {
            if (this.TryGetMethodHandle(def, out handle))
            {
                return true;
            }

            def = (Cci.IMethodDefinition)this.mapToPrevious.MapDefinition(def);
            if (def != null)
            {
                uint methodIndex;
                if (baseline.MethodsAdded.TryGetValue(def, out methodIndex))
                {
                    handle = MetadataTokens.MethodHandle((int)methodIndex);
                    return true;
                }
            }

            handle = default(MethodHandle);
            return false;
        }
Exemple #30
0
 internal override EmitDifferenceResult EmitDifference(EmitBaseline baseline, IEnumerable<SemanticEdit> edits, Func<ISymbol, bool> isAddedSymbol, Stream metadataStream, Stream ilStream, Stream pdbStream, ICollection<MethodDefinitionHandle> updatedMethodHandles, CompilationTestData testData, CancellationToken cancellationToken)
 {
     throw new NotImplementedException();
 }
        internal static CompilationDifference EmitDifference(
            this Compilation compilation,
            EmitBaseline baseline,
            ImmutableArray<SemanticEdit> edits)
        {
            Stream pdbStream;
            CompilationTestData testData = new CompilationTestData();

            var pdbName = Path.ChangeExtension(compilation.SourceModule.Name, "pdb");
            pdbStream = new MemoryStream();
            using (var pdbWriter = new Cci.PdbWriter(pdbName, new ComStreamWrapper(pdbStream)))
            {
                using (MemoryStream mdStream = new MemoryStream(), ilStream = new MemoryStream())
                {
                    var updatedMethodTokens = new List<uint>();
                    var result = compilation.EmitDifference(
                        baseline,
                        edits,
                        mdStream,
                        ilStream,
                        pdbStream,
                        updatedMethodTokens,
                        testData,
                        default(CancellationToken));

                    pdbStream.Seek(0, SeekOrigin.Begin);

                    return new CompilationDifference(
                        mdStream.ToImmutable(),
                        ilStream.ToImmutable(),
                        pdbStream,
                        result.Baseline,
                        testData,
                        result);
                }
            }
        }
        private EmitBaseline.MetadataSymbols GetOrCreateMetadataSymbols(EmitBaseline initialBaseline, CSharpCompilation compilation)
        {
            if (initialBaseline.LazyMetadataSymbols != null)
            {
                return initialBaseline.LazyMetadataSymbols;
            }

            var originalMetadata = initialBaseline.OriginalMetadata;

            // The purpose of this compilation is to provide PE symbols for original metadata.
            // We need to transfer the references from the current source compilation but don't need its syntax trees.
            var metadataCompilation = compilation.RemoveAllSyntaxTrees();

            var metadataAssembly = metadataCompilation.GetBoundReferenceManager().CreatePEAssemblyForAssemblyMetadata(AssemblyMetadata.Create(originalMetadata), MetadataImportOptions.All);
            var metadataDecoder = new MetadataDecoder(metadataAssembly.PrimaryModule);
            var metadataAnonymousTypes = GetAnonymousTypeMapFromMetadata(originalMetadata.MetadataReader, metadataDecoder);
            var metadataSymbols = new EmitBaseline.MetadataSymbols(metadataAnonymousTypes, metadataDecoder);

            return InterlockedOperations.Initialize(ref initialBaseline.LazyMetadataSymbols, metadataSymbols);
        }
 internal override EmitDifferenceResult EmitDifference(
     EmitBaseline baseline,
     IEnumerable<SemanticEdit> edits,
     Stream metadataStream,
     Stream ilStream,
     Stream pdbStream,
     ICollection<uint> updatedMethodTokens,
     CompilationTestData testData,
     CancellationToken cancellationToken)
 {
     return EmitHelpers.EmitDifference(
         this,
         baseline,
         edits,
         metadataStream,
         ilStream,
         pdbStream,
         updatedMethodTokens,
         testData,
         cancellationToken);
 }
Exemple #34
0
 internal EmitResult(bool success, ImmutableArray <Diagnostic> diagnostics, EmitBaseline generation)
 {
     this.success     = success;
     this.diagnostics = diagnostics;
     this.baseline    = generation;
 }