@@ -54,6 +54,19 @@ protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
5454 => IntPtr . Zero ;
5555 }
5656
57+ private readonly struct CompiledQuery ( MemoryStream peStream , MemoryStream pdbStream , SourceText text ) : IDisposable
58+ {
59+ public MemoryStream PEStream { get ; } = peStream ;
60+ public MemoryStream PdbStream { get ; } = pdbStream ;
61+ public SourceText Text { get ; } = text ;
62+
63+ public void Dispose ( )
64+ {
65+ PEStream . Dispose ( ) ;
66+ PdbStream . Dispose ( ) ;
67+ }
68+ }
69+
5770 /// <summary>
5871 /// Mapping from the parameter type of the <c>Find</c> method to the <see cref="QueryKind"/> value.
5972 /// </summary>
@@ -66,76 +79,93 @@ protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
6679 . Add ( typeof ( IPropertySymbol ) , QueryKind . Property )
6780 . Add ( typeof ( IEventSymbol ) , QueryKind . Event ) ;
6881
82+ private ImmutableDictionary < CompiledQueryId , CompiledQuery > _compiledQueries = ImmutableDictionary < CompiledQueryId , CompiledQuery > . Empty ;
83+
6984 protected abstract Compilation CreateCompilation ( SourceText query , IEnumerable < MetadataReference > references , SolutionServices services , out SyntaxTree queryTree , CancellationToken cancellationToken ) ;
7085
71- public async Task < ExecuteQueryResult > ExecuteQueryAsync (
72- Solution solution ,
86+ public CompileQueryResult CompileQuery (
87+ SolutionServices services ,
7388 string query ,
7489 string referenceAssembliesDir ,
75- ISemanticSearchResultsObserver observer ,
76- OptionsProvider < ClassificationOptions > classificationOptions ,
7790 TraceSource traceSource ,
7891 CancellationToken cancellationToken )
7992 {
80- try
81- {
82- // add progress items - one for compilation, one for emit and one for each project:
83- var remainingProgressItemCount = 2 + solution . ProjectIds . Count ;
84- await observer . AddItemsAsync ( remainingProgressItemCount , cancellationToken ) . ConfigureAwait ( false ) ;
85-
86- var metadataService = solution . Services . GetRequiredService < IMetadataService > ( ) ;
87- var metadataReferences = SemanticSearchUtilities . GetMetadataReferences ( metadataService , referenceAssembliesDir ) ;
88- var queryText = SemanticSearchUtilities . CreateSourceText ( query ) ;
89- var queryCompilation = CreateCompilation ( queryText , metadataReferences , solution . Services , out var queryTree , cancellationToken ) ;
93+ var metadataService = services . GetRequiredService < IMetadataService > ( ) ;
94+ var metadataReferences = SemanticSearchUtilities . GetMetadataReferences ( metadataService , referenceAssembliesDir ) ;
95+ var queryText = SemanticSearchUtilities . CreateSourceText ( query ) ;
96+ var queryCompilation = CreateCompilation ( queryText , metadataReferences , services , out var queryTree , cancellationToken ) ;
9097
91- cancellationToken . ThrowIfCancellationRequested ( ) ;
98+ cancellationToken . ThrowIfCancellationRequested ( ) ;
9299
93- // complete compilation progress item:
94- remainingProgressItemCount -- ;
95- await observer . ItemsCompletedAsync ( 1 , cancellationToken ) . ConfigureAwait ( false ) ;
100+ var emitOptions = new EmitOptions (
101+ debugInformationFormat : DebugInformationFormat . PortablePdb ,
102+ instrumentationKinds : [ InstrumentationKind . StackOverflowProbing , InstrumentationKind . ModuleCancellation ] ) ;
96103
97- var emitOptions = new EmitOptions (
98- debugInformationFormat : DebugInformationFormat . PortablePdb ,
99- instrumentationKinds : [ InstrumentationKind . StackOverflowProbing , InstrumentationKind . ModuleCancellation ] ) ;
104+ var peStream = new MemoryStream ( ) ;
105+ var pdbStream = new MemoryStream ( ) ;
100106
101- using var peStream = new MemoryStream ( ) ;
102- using var pdbStream = new MemoryStream ( ) ;
103-
104- var emitDifferenceTimer = SharedStopwatch . StartNew ( ) ;
105- var emitResult = queryCompilation . Emit ( peStream , pdbStream , options : emitOptions , cancellationToken : cancellationToken ) ;
106- var emitTime = emitDifferenceTimer . Elapsed ;
107-
108- var executionTime = TimeSpan . Zero ;
107+ var emitDifferenceTimer = SharedStopwatch . StartNew ( ) ;
108+ var emitResult = queryCompilation . Emit ( peStream , pdbStream , options : emitOptions , cancellationToken : cancellationToken ) ;
109+ var emitTime = emitDifferenceTimer . Elapsed ;
109110
110- cancellationToken . ThrowIfCancellationRequested ( ) ;
111+ CompiledQueryId queryId ;
112+ ImmutableArray < QueryCompilationError > errors ;
113+ if ( emitResult . Success )
114+ {
115+ queryId = CompiledQueryId . Create ( queryCompilation . Language ) ;
116+ Contract . ThrowIfFalse ( ImmutableInterlocked . TryAdd ( ref _compiledQueries , queryId , new CompiledQuery ( peStream , pdbStream , queryText ) ) ) ;
111117
112- // complete compilation progress item:
113- remainingProgressItemCount -- ;
114- await observer . ItemsCompletedAsync ( 1 , cancellationToken ) . ConfigureAwait ( false ) ;
118+ errors = [ ] ;
119+ }
120+ else
121+ {
122+ queryId = default ;
115123
116- if ( ! emitResult . Success )
124+ foreach ( var diagnostic in emitResult . Diagnostics )
117125 {
118- foreach ( var diagnostic in emitResult . Diagnostics )
126+ if ( diagnostic . Severity == DiagnosticSeverity . Error )
119127 {
120- if ( diagnostic . Severity == DiagnosticSeverity . Error )
121- {
122- traceSource . TraceInformation ( $ "Semantic search query compilation failed: { diagnostic } ") ;
123- }
128+ traceSource . TraceInformation ( $ "Semantic search query compilation failed: { diagnostic } ") ;
124129 }
130+ }
125131
126- var errors = emitResult . Diagnostics . SelectAsArray (
127- d => d . Severity == DiagnosticSeverity . Error ,
128- d => new QueryCompilationError ( d . Id , d . GetMessage ( ) , ( d . Location . SourceTree == queryTree ) ? d . Location . SourceSpan : default ) ) ;
132+ errors = emitResult . Diagnostics . SelectAsArray (
133+ d => d . Severity == DiagnosticSeverity . Error ,
134+ d => new QueryCompilationError ( d . Id , d . GetMessage ( ) , ( d . Location . SourceTree == queryTree ) ? d . Location . SourceSpan : default ) ) ;
135+ }
129136
130- return CreateResult ( errors , FeaturesResources . Semantic_search_query_failed_to_compile ) ;
131- }
137+ return new CompileQueryResult ( queryId , errors , emitTime ) ;
138+ }
139+
140+ public void DiscardQuery ( CompiledQueryId queryId )
141+ {
142+ Contract . ThrowIfFalse ( ImmutableInterlocked . TryRemove ( ref _compiledQueries , queryId , out var compiledQuery ) ) ;
143+ compiledQuery . Dispose ( ) ;
144+ }
132145
133- peStream . Position = 0 ;
134- pdbStream . Position = 0 ;
146+ public async Task < ExecuteQueryResult > ExecuteQueryAsync (
147+ Solution solution ,
148+ CompiledQueryId queryId ,
149+ ISemanticSearchResultsObserver observer ,
150+ OptionsProvider < ClassificationOptions > classificationOptions ,
151+ TraceSource traceSource ,
152+ CancellationToken cancellationToken )
153+ {
154+ Contract . ThrowIfFalse ( ImmutableInterlocked . TryRemove ( ref _compiledQueries , queryId , out var query ) ) ;
155+
156+ try
157+ {
158+ var executionTime = TimeSpan . Zero ;
159+
160+ var remainingProgressItemCount = solution . ProjectIds . Count ;
161+ await observer . AddItemsAsync ( remainingProgressItemCount , cancellationToken ) . ConfigureAwait ( false ) ;
162+
163+ query . PEStream . Position = 0 ;
164+ query . PdbStream . Position = 0 ;
135165 var loadContext = new LoadContext ( ) ;
136166 try
137167 {
138- var queryAssembly = loadContext . LoadFromStream ( peStream , pdbStream ) ;
168+ var queryAssembly = loadContext . LoadFromStream ( query . PEStream , query . PdbStream ) ;
139169 SetModuleCancellationToken ( queryAssembly , cancellationToken ) ;
140170
141171 SetToolImplementations (
@@ -146,17 +176,17 @@ public async Task<ExecuteQueryResult> ExecuteQueryAsync(
146176 if ( ! TryGetFindMethod ( queryAssembly , out var findMethod , out var queryKind , out var errorMessage , out var errorMessageArgs ) )
147177 {
148178 traceSource . TraceInformation ( $ "Semantic search failed: { errorMessage } ") ;
149- return CreateResult ( compilationErrors : [ ] , errorMessage , errorMessageArgs ) ;
179+ return CreateResult ( errorMessage , errorMessageArgs ) ;
150180 }
151181
152- var invocationContext = new QueryExecutionContext ( queryText , findMethod , observer , classificationOptions , traceSource ) ;
182+ var invocationContext = new QueryExecutionContext ( query . Text , findMethod , observer , classificationOptions , traceSource ) ;
153183 try
154184 {
155185 await invocationContext . InvokeAsync ( solution , queryKind , cancellationToken ) . ConfigureAwait ( false ) ;
156186
157187 if ( invocationContext . TerminatedWithException )
158188 {
159- return CreateResult ( compilationErrors : [ ] , FeaturesResources . Semantic_search_query_terminated_with_exception ) ;
189+ return CreateResult ( FeaturesResources . Semantic_search_query_terminated_with_exception ) ;
160190 }
161191 }
162192 finally
@@ -176,15 +206,19 @@ public async Task<ExecuteQueryResult> ExecuteQueryAsync(
176206 }
177207 }
178208
179- return CreateResult ( compilationErrors : [ ] , errorMessage : null ) ;
209+ return CreateResult ( errorMessage : null ) ;
180210
181- ExecuteQueryResult CreateResult ( ImmutableArray < QueryCompilationError > compilationErrors , string ? errorMessage , params string [ ] ? args )
182- => new ( compilationErrors , errorMessage , args , emitTime , executionTime ) ;
211+ ExecuteQueryResult CreateResult ( string ? errorMessage , params string [ ] ? args )
212+ => new ( errorMessage , args , executionTime ) ;
183213 }
184214 catch ( Exception e ) when ( FatalError . ReportAndPropagateUnlessCanceled ( e , cancellationToken , ErrorSeverity . Critical ) )
185215 {
186216 throw ExceptionUtilities . Unreachable ( ) ;
187217 }
218+ finally
219+ {
220+ query . Dispose ( ) ;
221+ }
188222 }
189223
190224 private static void SetModuleCancellationToken ( Assembly queryAssembly , CancellationToken cancellationToken )
0 commit comments