/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. dnSpy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ // Calls some code in a background thread so initialization code gets a chance // to run, eg. Roslyn MEF code, jitting methods, etc. // The code editor benefits from this optimization since a lot of Roslyn code // gets called and initialized the first time its window is shown. // About 20-25MB extra memory usage in my little test but code editor starts a lot quicker. // // The following gets initialized: // - RoslynMefHostServices.DefaultServices // - Workspaces code needed by RoslynLanguageCompiler // - Classification code, used indirectly by RoslynLanguageCompiler // - Completion code // - Signature help code // - Quick info code using System; using System.Collections.Generic; using System.ComponentModel.Composition; using System.Diagnostics; using System.Reflection; using System.Threading; using System.Threading.Tasks; using dnSpy.Contracts.Extension; using dnSpy.Contracts.Text.Classification; using dnSpy.Contracts.Text.Tagging; using dnSpy.Contracts.Utilities; using dnSpy.Roslyn.Documentation; using dnSpy.Roslyn.Intellisense.Completions; using dnSpy.Roslyn.Intellisense.QuickInfo; using dnSpy.Roslyn.Intellisense.SignatureHelp; using dnSpy.Roslyn.Internal.SignatureHelp; using dnSpy.Roslyn.Text; using dnSpy.Roslyn.Text.Tagging; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.VisualBasic; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Tagging; namespace dnSpy.Roslyn.Optimizations { [ExportAutoLoaded] sealed class FirstUseOptimizationLoader : IAutoLoaded { [ImportingConstructor] FirstUseOptimizationLoader(IThemeClassificationTypeService themeClassificationTypeService, ITextBufferFactoryService textBufferFactoryService, IRoslynDocumentationProviderFactory docFactory, IRoslynDocumentChangedService roslynDocumentChangedService) { new FirstUseOptimization(themeClassificationTypeService, textBufferFactoryService, docFactory, roslynDocumentChangedService); } } sealed class FirstUseOptimization { static readonly string csharpCode = @" sealed class C { int Method() { ""hello"".Equals(""sighelp""); return 42; } } "; static readonly string visualBasicCode = @" Module Module1 Sub Method() Dim s As String = ""hello"" s.Equals(""sighelp"") Dim local As Integer = 42 End Sub End Module "; public FirstUseOptimization(IThemeClassificationTypeService themeClassificationTypeService, ITextBufferFactoryService textBufferFactoryService, IRoslynDocumentationProviderFactory docFactory, IRoslynDocumentChangedService roslynDocumentChangedService) { var buffer = textBufferFactoryService.CreateTextBuffer(); var tagger = new RoslynTagger(buffer, themeClassificationTypeService, roslynDocumentChangedService); Task.Run(() => InitializeAsync(buffer, tagger, docFactory)) .ContinueWith(t => { var ex = t.Exception; Debug2.Assert(ex is null); }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()); } static MetadataReference CreateMetadataReference(Assembly assembly, IRoslynDocumentationProviderFactory docFactory) => MetadataReference.CreateFromFile(assembly.Location, documentation: docFactory.TryCreate(assembly.Location)); async Task InitializeAsync(ITextBuffer buffer, ISynchronousTagger tagger, IRoslynDocumentationProviderFactory docFactory) { ProfileOptimizationHelper.StartProfile("startup-roslyn"); var refs = new MetadataReference[] { CreateMetadataReference(typeof(int).Assembly, docFactory), }; await InitializeAsync(buffer, csharpCode, refs, LanguageNames.CSharp, tagger, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true), new CSharpParseOptions()); await InitializeAsync(buffer, visualBasicCode, refs, LanguageNames.VisualBasic, tagger, new VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary), new VisualBasicParseOptions()); (tagger as IDisposable)?.Dispose(); } async Task InitializeAsync(ITextBuffer buffer, string code, MetadataReference[] refs, string languageName, ISynchronousTagger tagger, CompilationOptions compilationOptions, ParseOptions parseOptions) { using (var workspace = new AdhocWorkspace(RoslynMefHostServices.DefaultServices)) { var documents = new List(); var projectId = ProjectId.CreateNewId(); documents.Add(DocumentInfo.Create(DocumentId.CreateNewId(projectId), "main.cs", null, SourceCodeKind.Regular, TextLoader.From(buffer.AsTextContainer(), VersionStamp.Create()))); var projectInfo = ProjectInfo.Create(projectId, VersionStamp.Create(), "compilecodeproj", Guid.NewGuid().ToString(), languageName, compilationOptions: compilationOptions .WithOptimizationLevel(OptimizationLevel.Release) .WithPlatform(Platform.AnyCpu) .WithAssemblyIdentityComparer(DesktopAssemblyIdentityComparer.Default), parseOptions: parseOptions, documents: documents, metadataReferences: refs, isSubmission: false, hostObjectType: null); workspace.AddProject(projectInfo); foreach (var doc in documents) workspace.OpenDocument(doc.Id); buffer.Replace(new Span(0, buffer.CurrentSnapshot.Length), code); { // Initialize classification code paths var spans = new NormalizedSnapshotSpanCollection(new SnapshotSpan(buffer.CurrentSnapshot, 0, buffer.CurrentSnapshot.Length)); foreach (var tagSpan in tagger.GetTags(spans, CancellationToken.None)) { } } { // Initialize completion code paths var info = CompletionInfo.Create(buffer.CurrentSnapshot); Debug2.Assert(info is not null); if (info is not null) { var completionTrigger = CompletionTrigger.Invoke; var completionList = await info.Value.CompletionService.GetCompletionsAsync(info.Value.Document, 0, completionTrigger); } } { // Initialize signature help code paths var info = SignatureHelpInfo.Create(buffer.CurrentSnapshot); Debug2.Assert(info is not null); if (info is not null) { int sigHelpIndex = code.IndexOf("sighelp"); Debug.Assert(sigHelpIndex >= 0); var triggerInfo = new SignatureHelpTriggerInfo(SignatureHelpTriggerReason.InvokeSignatureHelpCommand); var items = await info.Value.SignatureHelpService.GetItemsAsync(info.Value.Document, sigHelpIndex, triggerInfo); } } { // Initialize quick info code paths var info = QuickInfoState.Create(buffer.CurrentSnapshot); Debug2.Assert(info is not null); if (info is not null) { int quickInfoIndex = code.IndexOf("Equals"); Debug.Assert(quickInfoIndex >= 0); var item = await info.Value.QuickInfoService.GetItemAsync(info.Value.Document, quickInfoIndex); } } } } } }