// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Xml.Linq; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Options; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Execution { internal abstract class AbstractOptionsSerializationService : IOptionsSerializationService { public abstract void WriteTo(CompilationOptions options, ObjectWriter writer, CancellationToken cancellationToken); public abstract void WriteTo(ParseOptions options, ObjectWriter writer, CancellationToken cancellationToken); public abstract void WriteTo(OptionSet options, ObjectWriter writer, CancellationToken cancellationToken); public abstract CompilationOptions ReadCompilationOptionsFrom(ObjectReader reader, CancellationToken cancellationToken); public abstract ParseOptions ReadParseOptionsFrom(ObjectReader reader, CancellationToken cancellationToken); public abstract OptionSet ReadOptionSetFrom(ObjectReader reader, CancellationToken cancellationToken); protected void WriteCompilationOptionsTo(CompilationOptions options, ObjectWriter writer, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); writer.WriteInt32((int)options.OutputKind); writer.WriteBoolean(options.ReportSuppressedDiagnostics); writer.WriteString(options.ModuleName); writer.WriteString(options.MainTypeName); writer.WriteString(options.ScriptClassName); writer.WriteInt32((int)options.OptimizationLevel); writer.WriteBoolean(options.CheckOverflow); // REVIEW: is it okay this being not part of snapshot? writer.WriteString(options.CryptoKeyContainer); writer.WriteString(options.CryptoKeyFile); writer.WriteValue(options.CryptoPublicKey.ToArray()); writer.WriteBoolean(options.DelaySign.HasValue); if (options.DelaySign.HasValue) { writer.WriteBoolean(options.DelaySign.Value); } writer.WriteInt32((int)options.Platform); writer.WriteInt32((int)options.GeneralDiagnosticOption); writer.WriteInt32(options.WarningLevel); // REVIEW: I don't think there is a guarantee on ordering of elements in the immutable dictionary. // unfortunately, we need to sort them to make it deterministic writer.WriteInt32(options.SpecificDiagnosticOptions.Count); foreach (var kv in options.SpecificDiagnosticOptions.OrderBy(o => o.Key)) { writer.WriteString(kv.Key); writer.WriteInt32((int)kv.Value); } writer.WriteBoolean(options.ConcurrentBuild); writer.WriteBoolean(options.Deterministic); writer.WriteBoolean(options.PublicSign); // REVIEW: What should I do with these. we probably need to implement either out own one // or somehow share these as service.... // // XmlReferenceResolver xmlReferenceResolver // SourceReferenceResolver sourceReferenceResolver // MetadataReferenceResolver metadataReferenceResolver // AssemblyIdentityComparer assemblyIdentityComparer // StrongNameProvider strongNameProvider } protected void ReadCompilationOptionsFrom( ObjectReader reader, out OutputKind outputKind, out bool reportSuppressedDiagnostics, out string moduleName, out string mainTypeName, out string scriptClassName, out OptimizationLevel optimizationLevel, out bool checkOverflow, out string cryptoKeyContainer, out string cryptoKeyFile, out ImmutableArray cryptoPublicKey, out bool? delaySign, out Platform platform, out ReportDiagnostic generalDiagnosticOption, out int warningLevel, out IEnumerable> specificDiagnosticOptions, out bool concurrentBuild, out bool deterministic, out bool publicSign, out XmlReferenceResolver xmlReferenceResolver, out SourceReferenceResolver sourceReferenceResolver, out MetadataReferenceResolver metadataReferenceResolver, out AssemblyIdentityComparer assemblyIdentityComparer, out StrongNameProvider strongNameProvider, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); outputKind = (OutputKind)reader.ReadInt32(); reportSuppressedDiagnostics = reader.ReadBoolean(); moduleName = reader.ReadString(); mainTypeName = reader.ReadString(); scriptClassName = reader.ReadString(); optimizationLevel = (OptimizationLevel)reader.ReadInt32(); checkOverflow = reader.ReadBoolean(); // REVIEW: is it okay this being not part of snapshot? cryptoKeyContainer = reader.ReadString(); cryptoKeyFile = reader.ReadString(); cryptoPublicKey = reader.ReadArray().ToImmutableArrayOrEmpty(); delaySign = reader.ReadBoolean() ? (bool?)reader.ReadBoolean() : null; platform = (Platform)reader.ReadInt32(); generalDiagnosticOption = (ReportDiagnostic)reader.ReadInt32(); warningLevel = reader.ReadInt32(); // REVIEW: I don't think there is a guarantee on ordering of elements in the immutable dictionary. // unfortunately, we need to sort them to make it deterministic // not sure why CompilationOptions uses SequencialEqual to check options equality // when ordering can change result of it even if contents are same. var count = reader.ReadInt32(); List> specificDiagnosticOptionsList = null; if (count > 0) { specificDiagnosticOptionsList = new List>(count); for (var i = 0; i < count; i++) { var key = reader.ReadString(); var value = (ReportDiagnostic)reader.ReadInt32(); specificDiagnosticOptionsList.Add(KeyValuePair.Create(key, value)); } } specificDiagnosticOptions = specificDiagnosticOptionsList ?? SpecializedCollections.EmptyEnumerable>(); concurrentBuild = reader.ReadBoolean(); deterministic = reader.ReadBoolean(); publicSign = reader.ReadBoolean(); // REVIEW: What should I do with these. are these service required when compilation is built ourselves, not through // compiler. xmlReferenceResolver = XmlFileResolver.Default; sourceReferenceResolver = SourceFileResolver.Default; metadataReferenceResolver = null; assemblyIdentityComparer = DesktopAssemblyIdentityComparer.Default; strongNameProvider = new DesktopStrongNameProvider(); } protected void WriteParseOptionsTo(ParseOptions options, ObjectWriter writer, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); writer.WriteInt32((int)options.Kind); writer.WriteInt32((int)options.DocumentationMode); // REVIEW: I don't think there is a guarantee on ordering of elements in the readonly dictionary. // unfortunately, we need to sort them to make it deterministic writer.WriteInt32(options.Features.Count); foreach (var kv in options.Features.OrderBy(o => o.Key)) { writer.WriteString(kv.Key); writer.WriteString(kv.Value); } } protected void ReadParseOptionsFrom( ObjectReader reader, out SourceCodeKind kind, out DocumentationMode documentationMode, out IEnumerable> features, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); kind = (SourceCodeKind)reader.ReadInt32(); documentationMode = (DocumentationMode)reader.ReadInt32(); // REVIEW: I don't think there is a guarantee on ordering of elements in the immutable dictionary. // unfortunately, we need to sort them to make it deterministic // not sure why ParseOptions uses SequencialEqual to check options equality // when ordering can change result of it even if contents are same. var count = reader.ReadInt32(); List> featuresList = null; if (count > 0) { featuresList = new List>(count); for (var i = 0; i < count; i++) { var key = reader.ReadString(); var value = reader.ReadString(); featuresList.Add(KeyValuePair.Create(key, value)); } } features = featuresList ?? SpecializedCollections.EmptyEnumerable>(); } protected void WriteOptionSetTo(OptionSet options, string language, ObjectWriter writer, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // order of options we serialize should be static so that we get deterministic checksum for options WriteOptionTo(options, language, CodeStyleOptions.QualifyFieldAccess, writer, cancellationToken); WriteOptionTo(options, language, CodeStyleOptions.QualifyPropertyAccess, writer, cancellationToken); WriteOptionTo(options, language, CodeStyleOptions.QualifyMethodAccess, writer, cancellationToken); WriteOptionTo(options, language, CodeStyleOptions.QualifyEventAccess, writer, cancellationToken); WriteOptionTo(options, language, CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInDeclaration, writer, cancellationToken); WriteOptionTo(options, language, CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInMemberAccess, writer, cancellationToken); WriteOptionTo(options, language, CodeStyleOptions.PreferCoalesceExpression, writer, cancellationToken); WriteOptionTo(options, language, CodeStyleOptions.PreferCollectionInitializer, writer, cancellationToken); WriteOptionTo(options, language, CodeStyleOptions.PreferExplicitTupleNames, writer, cancellationToken); WriteOptionTo(options, language, CodeStyleOptions.PreferInlinedVariableDeclaration, writer, cancellationToken); WriteOptionTo(options, language, CodeStyleOptions.PreferNullPropagation, writer, cancellationToken); WriteOptionTo(options, language, CodeStyleOptions.PreferObjectInitializer, writer, cancellationToken); WriteOptionTo(options, language, CodeStyleOptions.PreferThrowExpression, writer, cancellationToken); } protected OptionSet ReadOptionSetFrom(OptionSet options, string language, ObjectReader reader, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); options = ReadOptionFrom(options, language, CodeStyleOptions.QualifyFieldAccess, reader, cancellationToken); options = ReadOptionFrom(options, language, CodeStyleOptions.QualifyPropertyAccess, reader, cancellationToken); options = ReadOptionFrom(options, language, CodeStyleOptions.QualifyMethodAccess, reader, cancellationToken); options = ReadOptionFrom(options, language, CodeStyleOptions.QualifyEventAccess, reader, cancellationToken); options = ReadOptionFrom(options, language, CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInDeclaration, reader, cancellationToken); options = ReadOptionFrom(options, language, CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInMemberAccess, reader, cancellationToken); options = ReadOptionFrom(options, language, CodeStyleOptions.PreferCoalesceExpression, reader, cancellationToken); options = ReadOptionFrom(options, language, CodeStyleOptions.PreferCollectionInitializer, reader, cancellationToken); options = ReadOptionFrom(options, language, CodeStyleOptions.PreferExplicitTupleNames, reader, cancellationToken); options = ReadOptionFrom(options, language, CodeStyleOptions.PreferInlinedVariableDeclaration, reader, cancellationToken); options = ReadOptionFrom(options, language, CodeStyleOptions.PreferNullPropagation, reader, cancellationToken); options = ReadOptionFrom(options, language, CodeStyleOptions.PreferObjectInitializer, reader, cancellationToken); options = ReadOptionFrom(options, language, CodeStyleOptions.PreferThrowExpression, reader, cancellationToken); return options; } protected void WriteOptionTo(OptionSet options, Option> option, ObjectWriter writer, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var value = options.GetOption(option); writer.WriteString(value.ToXElement().ToString()); } protected OptionSet ReadOptionFrom(OptionSet options, Option> option, ObjectReader reader, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var xmlText = reader.ReadString(); var value = CodeStyleOption.FromXElement(XElement.Parse(xmlText)); return options.WithChangedOption(option, value); } private void WriteOptionTo(OptionSet options, string language, PerLanguageOption> option, ObjectWriter writer, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var value = options.GetOption(option, language); writer.WriteString(value.ToXElement().ToString()); } private OptionSet ReadOptionFrom(OptionSet options, string language, PerLanguageOption> option, ObjectReader reader, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var xmlText = reader.ReadString(); var value = CodeStyleOption.FromXElement(XElement.Parse(xmlText)); return options.WithChangedOption(option, language, value); } /// /// this is not real option set. it doesn't have all options defined in host. but only those /// we pre-selected. /// protected class SerializedPartialOptionSet : OptionSet { private readonly ImmutableDictionary _values; public SerializedPartialOptionSet() { _values = ImmutableDictionary.Empty; } private SerializedPartialOptionSet(ImmutableDictionary values) { _values = values; } public override object GetOption(OptionKey optionKey) { object value; Contract.ThrowIfFalse(_values.TryGetValue(optionKey, out value)); return value; } public override OptionSet WithChangedOption(OptionKey optionAndLanguage, object value) { return new SerializedPartialOptionSet(_values.SetItem(optionAndLanguage, value)); } internal override IEnumerable GetChangedOptions(OptionSet optionSet) { foreach (var kvp in _values) { var currentValue = optionSet.GetOption(kvp.Key); if (!object.Equals(currentValue, kvp.Value)) { yield return kvp.Key; } } } } } }