File: Serialization\SerializerService.cs
Web Access
Project: ..\..\..\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Concurrent;
using System.Composition;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Serialization
{
    internal partial class SerializerService : ISerializerService
    {
        [ExportWorkspaceServiceFactory(typeof(ISerializerService), layer: ServiceLayer.Default), Shared]
        internal sealed class Factory : IWorkspaceServiceFactory
        {
            [ImportingConstructor]
            [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
            public Factory()
            {
            }
 
            [Obsolete(MefConstruction.FactoryMethodMessage, error: true)]
            public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
                => new SerializerService(workspaceServices.SolutionServices);
        }
 
        private static readonly Func<WellKnownSynchronizationKind, string> s_logKind = k => k.ToString();
 
        private readonly SolutionServices _workspaceServices;
 
        private readonly ITemporaryStorageServiceInternal _storageService;
        private readonly ITextFactoryService _textService;
        private readonly IDocumentationProviderService? _documentationService;
        private readonly IAnalyzerAssemblyLoaderProvider _analyzerLoaderProvider;
 
        private readonly ConcurrentDictionary<string, IOptionsSerializationService> _lazyLanguageSerializationService;
 
        [Obsolete(MefConstruction.FactoryMethodMessage, error: true)]
        private protected SerializerService(SolutionServices workspaceServices)
        {
            _workspaceServices = workspaceServices;
 
            _storageService = workspaceServices.GetRequiredService<ITemporaryStorageServiceInternal>();
            _textService = workspaceServices.GetRequiredService<ITextFactoryService>();
            _analyzerLoaderProvider = workspaceServices.GetRequiredService<IAnalyzerAssemblyLoaderProvider>();
            _documentationService = workspaceServices.GetService<IDocumentationProviderService>();
 
            _lazyLanguageSerializationService = new ConcurrentDictionary<string, IOptionsSerializationService>(concurrencyLevel: 2, capacity: _workspaceServices.SupportedLanguages.Count());
        }
 
        public Checksum CreateChecksum(object value, CancellationToken cancellationToken)
        {
            var kind = value.GetWellKnownSynchronizationKind();
 
            using (Logger.LogBlock(FunctionId.Serializer_CreateChecksum, s_logKind, kind, cancellationToken))
            {
                cancellationToken.ThrowIfCancellationRequested();
 
                if (value is IChecksummedObject checksummedObject)
                {
                    return checksummedObject.Checksum;
                }
 
                switch (kind)
                {
                    case WellKnownSynchronizationKind.Null:
                        return Checksum.Null;
 
                    case WellKnownSynchronizationKind.CompilationOptions:
                    case WellKnownSynchronizationKind.ParseOptions:
                    case WellKnownSynchronizationKind.ProjectReference:
                    case WellKnownSynchronizationKind.SourceGeneratedDocumentIdentity:
                        return Checksum.Create(value, this);
 
                    case WellKnownSynchronizationKind.MetadataReference:
                        return Checksum.Create(CreateChecksum((MetadataReference)value, cancellationToken));
 
                    case WellKnownSynchronizationKind.AnalyzerReference:
                        return Checksum.Create(CreateChecksum((AnalyzerReference)value, cancellationToken));
 
                    case WellKnownSynchronizationKind.SerializableSourceText:
                        return Checksum.Create(((SerializableSourceText)value).GetChecksum());
 
                    case WellKnownSynchronizationKind.SourceText:
                        return Checksum.Create(((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, SolutionReplicationContext context, CancellationToken cancellationToken)
        {
            var kind = value.GetWellKnownSynchronizationKind();
 
            using (Logger.LogBlock(FunctionId.Serializer_Serialize, s_logKind, kind, cancellationToken))
            {
                cancellationToken.ThrowIfCancellationRequested();
 
                if (value is ChecksumWithChildren checksumWithChildren)
                {
                    SerializeChecksumWithChildren(checksumWithChildren, writer, cancellationToken);
                    return;
                }
 
                switch (kind)
                {
                    case WellKnownSynchronizationKind.Null:
                        // do nothing
                        return;
 
                    case WellKnownSynchronizationKind.SolutionAttributes:
                    case WellKnownSynchronizationKind.ProjectAttributes:
                    case WellKnownSynchronizationKind.DocumentAttributes:
                    case WellKnownSynchronizationKind.SourceGeneratedDocumentIdentity:
                        ((IObjectWritable)value).WriteTo(writer);
                        return;
 
                    case WellKnownSynchronizationKind.CompilationOptions:
                        SerializeCompilationOptions((CompilationOptions)value, writer, cancellationToken);
                        return;
 
                    case WellKnownSynchronizationKind.ParseOptions:
                        cancellationToken.ThrowIfCancellationRequested();
                        SerializeParseOptions((ParseOptions)value, writer);
                        return;
 
                    case WellKnownSynchronizationKind.ProjectReference:
                        SerializeProjectReference((ProjectReference)value, writer, cancellationToken);
                        return;
 
                    case WellKnownSynchronizationKind.MetadataReference:
                        SerializeMetadataReference((MetadataReference)value, writer, context, cancellationToken);
                        return;
 
                    case WellKnownSynchronizationKind.AnalyzerReference:
                        SerializeAnalyzerReference((AnalyzerReference)value, writer, cancellationToken: cancellationToken);
                        return;
 
                    case WellKnownSynchronizationKind.SerializableSourceText:
                        SerializeSourceText((SerializableSourceText)value, writer, context, cancellationToken);
                        return;
 
                    case WellKnownSynchronizationKind.SourceText:
                        SerializeSourceText(new SerializableSourceText((SourceText)value), writer, context, 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<T>(WellKnownSynchronizationKind kind, ObjectReader reader, CancellationToken cancellationToken)
        {
            using (Logger.LogBlock(FunctionId.Serializer_Deserialize, s_logKind, kind, cancellationToken))
            {
                cancellationToken.ThrowIfCancellationRequested();
 
                switch (kind)
                {
                    case WellKnownSynchronizationKind.Null:
                        return default;
 
                    case WellKnownSynchronizationKind.SolutionState:
                    case WellKnownSynchronizationKind.ProjectState:
                    case WellKnownSynchronizationKind.DocumentState:
                    case WellKnownSynchronizationKind.ChecksumCollection:
                        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.SourceGeneratedDocumentIdentity:
                        return (T)(object)SourceGeneratedDocumentIdentity.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.SerializableSourceText:
                        return (T)(object)SerializableSourceText.Deserialize(reader, _storageService, _textService, cancellationToken);
                    case WellKnownSynchronizationKind.SourceText:
                        return (T)(object)DeserializeSourceText(reader, cancellationToken);
 
                    default:
                        throw ExceptionUtilities.UnexpectedValue(kind);
                }
            }
        }
 
        private IOptionsSerializationService GetOptionsSerializationService(string languageName)
            => _lazyLanguageSerializationService.GetOrAdd(languageName, n => _workspaceServices.GetLanguageServices(n).GetRequiredService<IOptionsSerializationService>());
 
        public Checksum CreateParseOptionsChecksum(ParseOptions value)
            => Checksum.Create(value, this);
    }
 
    // TODO: convert this to sub class rather than using enum with if statement.
    internal enum SerializationKinds
    {
        Bits,
        FilePath,
        MemoryMapFile
    }
}