// 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.Collections.Concurrent; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Execution; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Serialization { /// /// serialize and deserialize objects to straem. /// some of these could be moved into actual object, but putting everything here is a bit easier to find I believe. /// /// also, consider moving this serializer to use C# BOND serializer /// https://github.com/Microsoft/bond /// internal partial class Serializer { private readonly HostWorkspaceServices _workspaceServices; private readonly IReferenceSerializationService _hostSerializationService; private readonly ITemporaryStorageService2 _tempService; private readonly ITextFactoryService _textService; private readonly ConcurrentDictionary _lazyLanguageSerializationService; public Serializer(Solution solution) : this(solution.Workspace) { } public Serializer(Workspace workspace) : this(workspace.Services) { } public Serializer(HostWorkspaceServices workspaceServices) { _workspaceServices = workspaceServices; _hostSerializationService = _workspaceServices.GetService(); _tempService = _workspaceServices.GetService() as ITemporaryStorageService2; _textService = _workspaceServices.GetService(); _lazyLanguageSerializationService = new ConcurrentDictionary(concurrencyLevel: 2, capacity: _workspaceServices.SupportedLanguages.Count()); } public Checksum CreateChecksum(object value, CancellationToken cancellationToken) { var kind = value.GetWellKnownSynchronizationKind(); using (Logger.LogBlock(FunctionId.Serializer_CreateChecksum, kind.ToString(), cancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); if (value is IChecksummedObject) { return ((IChecksummedObject)value).Checksum; } switch (kind) { case WellKnownSynchronizationKind.Null: return Checksum.Null; case WellKnownSynchronizationKind.CompilationOptions: case WellKnownSynchronizationKind.ParseOptions: case WellKnownSynchronizationKind.ProjectReference: return Checksum.Create(kind, value, this); case WellKnownSynchronizationKind.MetadataReference: return Checksum.Create(kind, _hostSerializationService.CreateChecksum((MetadataReference)value, cancellationToken)); case WellKnownSynchronizationKind.AnalyzerReference: return Checksum.Create(kind, _hostSerializationService.CreateChecksum((AnalyzerReference)value, cancellationToken)); case WellKnownSynchronizationKind.SourceText: return Checksum.Create(kind, ((SourceText)value).GetChecksum()); default: // object that is not part of solution is not supported since we don't know what inputs are required to // serialize it throw ExceptionUtilities.UnexpectedValue(kind); } } } public void Serialize(object value, ObjectWriter writer, CancellationToken cancellationToken) { var kind = value.GetWellKnownSynchronizationKind(); using (Logger.LogBlock(FunctionId.Serializer_Serialize, kind.ToString(), cancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); if (value is ChecksumWithChildren) { SerializeChecksumWithChildren((ChecksumWithChildren)value, writer, cancellationToken); return; } switch (kind) { case WellKnownSynchronizationKind.Null: // do nothing return; case WellKnownSynchronizationKind.SolutionAttributes: case WellKnownSynchronizationKind.ProjectAttributes: case WellKnownSynchronizationKind.DocumentAttributes: ((IObjectWritable)value).WriteTo(writer); return; case WellKnownSynchronizationKind.CompilationOptions: SerializeCompilationOptions((CompilationOptions)value, writer, cancellationToken); return; case WellKnownSynchronizationKind.ParseOptions: SerializeParseOptions((ParseOptions)value, writer, cancellationToken); return; case WellKnownSynchronizationKind.ProjectReference: SerializeProjectReference((ProjectReference)value, writer, cancellationToken); return; case WellKnownSynchronizationKind.MetadataReference: SerializeMetadataReference((MetadataReference)value, writer, cancellationToken); return; case WellKnownSynchronizationKind.AnalyzerReference: SerializeAnalyzerReference((AnalyzerReference)value, writer, usePathFromAssembly: true, cancellationToken: cancellationToken); return; case WellKnownSynchronizationKind.SourceText: SerializeSourceText(storage: null, text: (SourceText)value, writer: writer, cancellationToken: cancellationToken); return; default: // object that is not part of solution is not supported since we don't know what inputs are required to // serialize it throw ExceptionUtilities.UnexpectedValue(kind); } } } public T Deserialize(WellKnownSynchronizationKind kind, ObjectReader reader, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.Serializer_Deserialize, kind.ToString(), cancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); switch (kind) { case WellKnownSynchronizationKind.Null: return default(T); case WellKnownSynchronizationKind.SolutionState: case WellKnownSynchronizationKind.ProjectState: case WellKnownSynchronizationKind.DocumentState: case WellKnownSynchronizationKind.Projects: case WellKnownSynchronizationKind.Documents: case WellKnownSynchronizationKind.TextDocuments: case WellKnownSynchronizationKind.ProjectReferences: case WellKnownSynchronizationKind.MetadataReferences: case WellKnownSynchronizationKind.AnalyzerReferences: return (T)(object)DeserializeChecksumWithChildren(reader, cancellationToken); case WellKnownSynchronizationKind.SolutionAttributes: return (T)(object)SolutionInfo.SolutionAttributes.ReadFrom(reader); case WellKnownSynchronizationKind.ProjectAttributes: return (T)(object)ProjectInfo.ProjectAttributes.ReadFrom(reader); case WellKnownSynchronizationKind.DocumentAttributes: return (T)(object)DocumentInfo.DocumentAttributes.ReadFrom(reader); case WellKnownSynchronizationKind.CompilationOptions: return (T)(object)DeserializeCompilationOptions(reader, cancellationToken); case WellKnownSynchronizationKind.ParseOptions: return (T)(object)DeserializeParseOptions(reader, cancellationToken); case WellKnownSynchronizationKind.ProjectReference: return (T)(object)DeserializeProjectReference(reader, cancellationToken); case WellKnownSynchronizationKind.MetadataReference: return (T)(object)DeserializeMetadataReference(reader, cancellationToken); case WellKnownSynchronizationKind.AnalyzerReference: return (T)(object)DeserializeAnalyzerReference(reader, cancellationToken); case WellKnownSynchronizationKind.SourceText: return (T)(object)DeserializeSourceText(reader, cancellationToken); case WellKnownSynchronizationKind.OptionSet: return (T)(object)DeserializeOptionSet(reader, cancellationToken); default: throw ExceptionUtilities.UnexpectedValue(kind); } } } private IOptionsSerializationService GetOptionsSerializationService(string languageName) { return _lazyLanguageSerializationService.GetOrAdd(languageName, n => _workspaceServices.GetLanguageServices(n).GetService()); } } // TODO: convert this to sub class rather than using enum with if statement. internal enum SerializationKinds { Bits, FilePath, MemoryMapFile } }