File: ServiceDescriptors.cs
Web Access
Project: src\src\Workspaces\Remote\Core\Microsoft.CodeAnalysis.Remote.Workspaces.csproj (Microsoft.CodeAnalysis.Remote.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.Generic;
using System.Collections.Immutable;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis.AddImport;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.CodeFixes.FullyQualify;
using Microsoft.CodeAnalysis.CodeLens;
using Microsoft.CodeAnalysis.Completion.Providers;
using Microsoft.CodeAnalysis.ConvertTupleToStruct;
using Microsoft.CodeAnalysis.DesignerAttribute;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.DocumentHighlighting;
using Microsoft.CodeAnalysis.EditAndContinue;
using Microsoft.CodeAnalysis.EncapsulateField;
using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.InheritanceMargin;
using Microsoft.CodeAnalysis.LegacySolutionEvents;
using Microsoft.CodeAnalysis.NavigateTo;
using Microsoft.CodeAnalysis.NavigationBar;
using Microsoft.CodeAnalysis.RelatedDocuments;
using Microsoft.CodeAnalysis.Rename;
using Microsoft.CodeAnalysis.SemanticSearch;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.SourceGeneration;
using Microsoft.CodeAnalysis.StackTraceExplorer;
using Microsoft.CodeAnalysis.SymbolSearch;
using Microsoft.CodeAnalysis.TaskList;
using Microsoft.CodeAnalysis.UnusedReferences;
using Microsoft.CodeAnalysis.ValueTracking;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Remote
{
    /// <summary>
    /// Service descriptors of brokered Roslyn ServiceHub services.
    /// </summary>
    internal sealed class ServiceDescriptors
    {
        internal const string ComponentName = "LanguageServices";
 
        private const string InterfaceNamePrefix = "IRemote";
        private const string InterfaceNameSuffix = "Service";
 
        private const string Suffix64 = "64";
        private const string SuffixServerGC = "S";
        private const string SuffixCoreClr = "Core";
 
        public static readonly ServiceDescriptors Instance = new(ComponentName, GetFeatureDisplayName, RemoteSerializationOptions.Default, new (Type, Type?)[]
        {
            (typeof(IRemoteAssetSynchronizationService), null),
            (typeof(IRemoteAsynchronousOperationListenerService), null),
            (typeof(IRemoteCodeLensReferencesService), null),
            (typeof(IRemoteConvertTupleToStructCodeRefactoringService), null),
            (typeof(IRemoteDependentTypeFinderService), null),
            (typeof(IRemoteDesignerAttributeDiscoveryService), typeof(IRemoteDesignerAttributeDiscoveryService.ICallback)),
            (typeof(IRemoteDiagnosticAnalyzerService), null),
            (typeof(IRemoteDocumentHighlightsService), null),
            (typeof(IRemoteEditAndContinueService), typeof(IRemoteEditAndContinueService.ICallback)),
            (typeof(IRemoteEncapsulateFieldService), null),
            (typeof(IRemoteExtensionMethodImportCompletionService), null),
            (typeof(IRemoteFindUsagesService), typeof(IRemoteFindUsagesService.ICallback)),
            (typeof(IRemoteFullyQualifyService), null),
            (typeof(IRemoteInheritanceMarginService), null),
            (typeof(IRemoteKeepAliveService), null),
            (typeof(IRemoteLegacySolutionEventsAggregationService), null),
            (typeof(IRemoteMissingImportDiscoveryService), typeof(IRemoteMissingImportDiscoveryService.ICallback)),
            (typeof(IRemoteNavigateToSearchService), typeof(IRemoteNavigateToSearchService.ICallback)),
            (typeof(IRemoteNavigationBarItemService), null),
            (typeof(IRemoteProcessTelemetryService), null),
            (typeof(IRemoteRelatedDocumentsService), typeof(IRemoteRelatedDocumentsService.ICallback)),
            (typeof(IRemoteRenamerService), null),
            (typeof(IRemoteSemanticClassificationService), null),
            (typeof(IRemoteSemanticSearchService), typeof(IRemoteSemanticSearchService.ICallback)),
            (typeof(IRemoteSourceGenerationService), null),
            (typeof(IRemoteStackTraceExplorerService), null),
            (typeof(IRemoteSymbolFinderService), typeof(IRemoteSymbolFinderService.ICallback)),
            (typeof(IRemoteSymbolSearchUpdateService), null),
            (typeof(IRemoteTaskListService), null),
            (typeof(IRemoteUnitTestingSearchService), null),
            (typeof(IRemoteUnusedReferenceAnalysisService), null),
            (typeof(IRemoteValueTrackingService), null),
        });
 
        internal readonly RemoteSerializationOptions Options;
        private readonly ImmutableDictionary<Type, (ServiceDescriptor descriptorCoreClr64, ServiceDescriptor descriptorCoreClr64ServerGC)> _descriptors;
        private readonly string _componentName;
        private readonly Func<string, string> _featureDisplayNameProvider;
 
        public ServiceDescriptors(
            string componentName,
            Func<string, string> featureDisplayNameProvider,
            RemoteSerializationOptions serializationOptions,
            IEnumerable<(Type serviceInterface, Type? callbackInterface)> interfaces)
        {
            Options = serializationOptions;
            _componentName = componentName;
            _featureDisplayNameProvider = featureDisplayNameProvider;
            _descriptors = interfaces.ToImmutableDictionary(i => i.serviceInterface, i => CreateDescriptors(i.serviceInterface, i.callbackInterface));
        }
 
        internal static string GetSimpleName(Type serviceInterface)
        {
            Contract.ThrowIfFalse(serviceInterface.IsInterface);
            var interfaceName = serviceInterface.Name;
            Contract.ThrowIfFalse(interfaceName.StartsWith(InterfaceNamePrefix, StringComparison.Ordinal));
            Contract.ThrowIfFalse(interfaceName.EndsWith(InterfaceNameSuffix, StringComparison.Ordinal));
 
            return interfaceName.Substring(InterfaceNamePrefix.Length, interfaceName.Length - InterfaceNamePrefix.Length - InterfaceNameSuffix.Length);
        }
 
        private (ServiceDescriptor descriptorCoreClr64, ServiceDescriptor descriptorCoreClr64ServerGC) CreateDescriptors(Type serviceInterface, Type? callbackInterface)
        {
            Contract.ThrowIfFalse(callbackInterface == null || callbackInterface.IsInterface);
 
            var simpleName = GetSimpleName(serviceInterface);
            var descriptorCoreClr64 = ServiceDescriptor.CreateRemoteServiceDescriptor(_componentName, simpleName, SuffixCoreClr + Suffix64, Options, _featureDisplayNameProvider, callbackInterface);
            var descriptorCoreClr64ServerGC = ServiceDescriptor.CreateRemoteServiceDescriptor(_componentName, simpleName, SuffixCoreClr + Suffix64 + SuffixServerGC, Options, _featureDisplayNameProvider, callbackInterface);
 
            return (descriptorCoreClr64, descriptorCoreClr64ServerGC);
        }
 
        public static bool IsCurrentProcessRunningOnCoreClr()
            => !RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework") &&
               !RuntimeInformation.FrameworkDescription.StartsWith(".NET Native");
 
        public ServiceDescriptor GetServiceDescriptorForServiceFactory(Type serviceType)
            => GetServiceDescriptor(serviceType, RemoteProcessConfiguration.ServerGC);
 
        public ServiceDescriptor GetServiceDescriptor(Type serviceType, RemoteProcessConfiguration configuration)
        {
            if (!_descriptors.TryGetValue(serviceType, out var descriptor))
            {
                throw ExceptionUtilities.UnexpectedValue(serviceType);
            }
 
            var (descriptorCoreClr64, descriptorCoreClr64ServerGC) = descriptor;
            return (configuration & RemoteProcessConfiguration.ServerGC) switch
            {
                0 => descriptorCoreClr64,
                RemoteProcessConfiguration.ServerGC => descriptorCoreClr64ServerGC,
                _ => throw ExceptionUtilities.Unreachable()
            };
        }
 
        /// <summary>
        /// <paramref name="serviceName"/> is a short service name, e.g. "EditAndContinue".
        /// </summary>
        internal static string GetFeatureDisplayName(string serviceName)
            => RemoteWorkspacesResources.GetResourceString("FeatureName_" + serviceName);
 
        internal TestAccessor GetTestAccessor()
            => new(this);
 
        internal readonly struct TestAccessor
        {
            private readonly ServiceDescriptors _serviceDescriptors;
 
            internal TestAccessor(ServiceDescriptors serviceDescriptors)
                => _serviceDescriptors = serviceDescriptors;
 
            public ImmutableDictionary<Type, (ServiceDescriptor descriptorCoreClr64, ServiceDescriptor descriptorCoreClr64ServerGC)> Descriptors
                => _serviceDescriptors._descriptors;
        }
    }
}