diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumCollection.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumCollection.cs index e2a2d6fef2a46..41fcd81127cef 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumCollection.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumCollection.cs @@ -58,7 +58,7 @@ internal static async Task FindAsync( AssetPath assetPath, TextDocumentStates documentStates, HashSet searchingChecksumsLeft, - Dictionary result, + Action onAssetFound, CancellationToken cancellationToken) where TState : TextDocumentState { var hintDocument = assetPath.DocumentId; @@ -68,7 +68,7 @@ internal static async Task FindAsync( if (state != null) { Contract.ThrowIfFalse(state.TryGetStateChecksums(out var stateChecksums)); - await stateChecksums.FindAsync(assetPath, state, searchingChecksumsLeft, result, cancellationToken).ConfigureAwait(false); + await stateChecksums.FindAsync(assetPath, state, searchingChecksumsLeft, onAssetFound, cancellationToken).ConfigureAwait(false); } } else @@ -81,7 +81,7 @@ internal static async Task FindAsync( Contract.ThrowIfFalse(state.TryGetStateChecksums(out var stateChecksums)); - await stateChecksums.FindAsync(assetPath, state, searchingChecksumsLeft, result, cancellationToken).ConfigureAwait(false); + await stateChecksums.FindAsync(assetPath, state, searchingChecksumsLeft, onAssetFound, cancellationToken).ConfigureAwait(false); } } } @@ -90,7 +90,7 @@ internal static void Find( IReadOnlyList values, ChecksumCollection checksums, HashSet searchingChecksumsLeft, - Dictionary result, + Action onAssetFound, CancellationToken cancellationToken) where T : class { Contract.ThrowIfFalse(values.Count == checksums.Children.Length); @@ -103,7 +103,7 @@ internal static void Find( var checksum = checksums.Children[i]; if (searchingChecksumsLeft.Remove(checksum)) - result[checksum] = values[i]; + onAssetFound(checksum, values[i]); } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs b/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs index 7762b7c92f61f..2a532f588897c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs @@ -118,7 +118,7 @@ public async Task FindAsync( ProjectCone? projectCone, AssetPath assetPath, HashSet searchingChecksumsLeft, - Dictionary result, + Action onAssetFound, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -128,10 +128,10 @@ public async Task FindAsync( if (assetPath.IncludeSolutionCompilationState) { if (assetPath.IncludeSolutionCompilationStateChecksums && searchingChecksumsLeft.Remove(this.Checksum)) - result[this.Checksum] = this; + onAssetFound(this.Checksum, this); if (assetPath.IncludeSolutionSourceGeneratorExecutionVersionMap && searchingChecksumsLeft.Remove(this.SourceGeneratorExecutionVersionMap)) - result[this.SourceGeneratorExecutionVersionMap] = compilationState.SourceGeneratorExecutionVersionMap; + onAssetFound(this.SourceGeneratorExecutionVersionMap, compilationState.SourceGeneratorExecutionVersionMap); if (compilationState.FrozenSourceGeneratedDocumentStates != null) { @@ -143,7 +143,7 @@ public async Task FindAsync( { await ChecksumCollection.FindAsync( new AssetPath(AssetPathKind.DocumentText, assetPath.ProjectId, assetPath.DocumentId), - compilationState.FrozenSourceGeneratedDocumentStates, searchingChecksumsLeft, result, cancellationToken).ConfigureAwait(false); + compilationState.FrozenSourceGeneratedDocumentStates, searchingChecksumsLeft, onAssetFound, cancellationToken).ConfigureAwait(false); } // ... or one of the identities. In this case, we'll use the fact that there's a 1:1 correspondence between the @@ -161,7 +161,7 @@ await ChecksumCollection.FindAsync( if (searchingChecksumsLeft.Remove(identityChecksum)) { Contract.ThrowIfFalse(compilationState.FrozenSourceGeneratedDocumentStates.TryGetState(documentId, out var state)); - result[identityChecksum] = state.Identity; + onAssetFound(identityChecksum, state.Identity); } } } @@ -175,7 +175,7 @@ await ChecksumCollection.FindAsync( { var id = FrozenSourceGeneratedDocuments.Value.Ids[i]; Contract.ThrowIfFalse(compilationState.FrozenSourceGeneratedDocumentStates.TryGetState(id, out var state)); - result[identityChecksum] = state.Identity; + onAssetFound(identityChecksum, state.Identity); } } } @@ -189,13 +189,13 @@ await ChecksumCollection.FindAsync( // If we're not in a project cone, start the search at the top most state-checksum corresponding to the // entire solution. Contract.ThrowIfFalse(solutionState.TryGetStateChecksums(out var solutionChecksums)); - await solutionChecksums.FindAsync(solutionState, projectCone, assetPath, searchingChecksumsLeft, result, cancellationToken).ConfigureAwait(false); + await solutionChecksums.FindAsync(solutionState, projectCone, assetPath, searchingChecksumsLeft, onAssetFound, cancellationToken).ConfigureAwait(false); } else { // Otherwise, grab the top-most state checksum for this cone and search within that. Contract.ThrowIfFalse(solutionState.TryGetStateChecksums(projectCone.RootProjectId, out var solutionChecksums)); - await solutionChecksums.FindAsync(solutionState, projectCone, assetPath, searchingChecksumsLeft, result, cancellationToken).ConfigureAwait(false); + await solutionChecksums.FindAsync(solutionState, projectCone, assetPath, searchingChecksumsLeft, onAssetFound, cancellationToken).ConfigureAwait(false); } } } @@ -269,7 +269,7 @@ public async Task FindAsync( ProjectCone? projectCone, AssetPath assetPath, HashSet searchingChecksumsLeft, - Dictionary result, + Action onAssetFound, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -279,13 +279,13 @@ public async Task FindAsync( if (assetPath.IncludeSolutionState) { if (assetPath.IncludeSolutionStateChecksums && searchingChecksumsLeft.Remove(Checksum)) - result[Checksum] = this; + onAssetFound(Checksum, this); if (assetPath.IncludeSolutionAttributes && searchingChecksumsLeft.Remove(Attributes)) - result[Attributes] = solution.SolutionAttributes; + onAssetFound(Attributes, solution.SolutionAttributes); if (assetPath.IncludeSolutionAnalyzerReferences) - ChecksumCollection.Find(solution.AnalyzerReferences, AnalyzerReferences, searchingChecksumsLeft, result, cancellationToken); + ChecksumCollection.Find(solution.AnalyzerReferences, AnalyzerReferences, searchingChecksumsLeft, onAssetFound, cancellationToken); } if (searchingChecksumsLeft.Count == 0) @@ -304,7 +304,7 @@ public async Task FindAsync( if (projectState != null && projectState.TryGetStateChecksums(out var projectStateChecksums)) { - await projectStateChecksums.FindAsync(projectState, assetPath, searchingChecksumsLeft, result, cancellationToken).ConfigureAwait(false); + await projectStateChecksums.FindAsync(projectState, assetPath, searchingChecksumsLeft, onAssetFound, cancellationToken).ConfigureAwait(false); } } else @@ -327,7 +327,7 @@ public async Task FindAsync( if (!projectState.TryGetStateChecksums(out var projectStateChecksums)) continue; - await projectStateChecksums.FindAsync(projectState, assetPath, searchingChecksumsLeft, result, cancellationToken).ConfigureAwait(false); + await projectStateChecksums.FindAsync(projectState, assetPath, searchingChecksumsLeft, onAssetFound, cancellationToken).ConfigureAwait(false); } } } @@ -435,7 +435,7 @@ public async Task FindAsync( ProjectState state, AssetPath assetPath, HashSet searchingChecksumsLeft, - Dictionary result, + Action onAssetFound, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -449,32 +449,38 @@ public async Task FindAsync( if (assetPath.IncludeProjects) { if (assetPath.IncludeProjectStateChecksums && searchingChecksumsLeft.Remove(Checksum)) - result[Checksum] = this; + onAssetFound(Checksum, this); if (assetPath.IncludeProjectAttributes && searchingChecksumsLeft.Remove(Info)) - result[Info] = state.ProjectInfo.Attributes; + onAssetFound(Info, state.ProjectInfo.Attributes); if (assetPath.IncludeProjectCompilationOptions && searchingChecksumsLeft.Remove(CompilationOptions)) - result[CompilationOptions] = state.CompilationOptions ?? throw new InvalidOperationException("We should not be trying to serialize a project with no compilation options; RemoteSupportedLanguages.IsSupported should have filtered it out."); + { + var compilationOptions = state.CompilationOptions ?? throw new InvalidOperationException("We should not be trying to serialize a project with no compilation options; RemoteSupportedLanguages.IsSupported should have filtered it out."); + onAssetFound(CompilationOptions, compilationOptions); + } if (assetPath.IncludeProjectParseOptions && searchingChecksumsLeft.Remove(ParseOptions)) - result[ParseOptions] = state.ParseOptions ?? throw new InvalidOperationException("We should not be trying to serialize a project with no parse options; RemoteSupportedLanguages.IsSupported should have filtered it out."); + { + var parseOptions = state.ParseOptions ?? throw new InvalidOperationException("We should not be trying to serialize a project with no parse options; RemoteSupportedLanguages.IsSupported should have filtered it out."); + onAssetFound(ParseOptions, parseOptions); + } if (assetPath.IncludeProjectProjectReferences) - ChecksumCollection.Find(state.ProjectReferences, ProjectReferences, searchingChecksumsLeft, result, cancellationToken); + ChecksumCollection.Find(state.ProjectReferences, ProjectReferences, searchingChecksumsLeft, onAssetFound, cancellationToken); if (assetPath.IncludeProjectMetadataReferences) - ChecksumCollection.Find(state.MetadataReferences, MetadataReferences, searchingChecksumsLeft, result, cancellationToken); + ChecksumCollection.Find(state.MetadataReferences, MetadataReferences, searchingChecksumsLeft, onAssetFound, cancellationToken); if (assetPath.IncludeProjectAnalyzerReferences) - ChecksumCollection.Find(state.AnalyzerReferences, AnalyzerReferences, searchingChecksumsLeft, result, cancellationToken); + ChecksumCollection.Find(state.AnalyzerReferences, AnalyzerReferences, searchingChecksumsLeft, onAssetFound, cancellationToken); } if (assetPath.IncludeDocuments) { - await ChecksumCollection.FindAsync(assetPath, state.DocumentStates, searchingChecksumsLeft, result, cancellationToken).ConfigureAwait(false); - await ChecksumCollection.FindAsync(assetPath, state.AdditionalDocumentStates, searchingChecksumsLeft, result, cancellationToken).ConfigureAwait(false); - await ChecksumCollection.FindAsync(assetPath, state.AnalyzerConfigDocumentStates, searchingChecksumsLeft, result, cancellationToken).ConfigureAwait(false); + await ChecksumCollection.FindAsync(assetPath, state.DocumentStates, searchingChecksumsLeft, onAssetFound, cancellationToken).ConfigureAwait(false); + await ChecksumCollection.FindAsync(assetPath, state.AdditionalDocumentStates, searchingChecksumsLeft, onAssetFound, cancellationToken).ConfigureAwait(false); + await ChecksumCollection.FindAsync(assetPath, state.AnalyzerConfigDocumentStates, searchingChecksumsLeft, onAssetFound, cancellationToken).ConfigureAwait(false); } } } @@ -500,7 +506,7 @@ public async Task FindAsync( AssetPath assetPath, TextDocumentState state, HashSet searchingChecksumsLeft, - Dictionary result, + Action onAssetFound, CancellationToken cancellationToken) { Debug.Assert(state.TryGetStateChecksums(out var stateChecksum) && this == stateChecksum); @@ -508,10 +514,13 @@ public async Task FindAsync( cancellationToken.ThrowIfCancellationRequested(); if (assetPath.IncludeDocumentAttributes && searchingChecksumsLeft.Remove(Info)) - result[Info] = state.Attributes; + onAssetFound(Info, state.Attributes); if (assetPath.IncludeDocumentText && searchingChecksumsLeft.Remove(Text)) - result[Text] = await SerializableSourceText.FromTextDocumentStateAsync(state, cancellationToken).ConfigureAwait(false); + { + var text = await SerializableSourceText.FromTextDocumentStateAsync(state, cancellationToken).ConfigureAwait(false); + onAssetFound(Text, text); + } } } diff --git a/src/Workspaces/Remote/Core/SolutionAssetStorage.Scope.cs b/src/Workspaces/Remote/Core/SolutionAssetStorage.Scope.cs index e4b6ae9f169a8..376028765c57a 100644 --- a/src/Workspaces/Remote/Core/SolutionAssetStorage.Scope.cs +++ b/src/Workspaces/Remote/Core/SolutionAssetStorage.Scope.cs @@ -58,14 +58,17 @@ public async Task AddAssetsAsync( var numberOfChecksumsToSearch = checksumsToFind.Count; Contract.ThrowIfTrue(checksumsToFind.Contains(Checksum.Null)); - await FindAssetsAsync(assetPath, checksumsToFind, assetMap, cancellationToken).ConfigureAwait(false); + await FindAssetsAsync( + assetPath, checksumsToFind, + (checksum, asset) => assetMap[checksum] = asset, + cancellationToken).ConfigureAwait(false); Contract.ThrowIfTrue(checksumsToFind.Count > 0); Contract.ThrowIfTrue(assetMap.Count != numberOfChecksumsToSearch); } private async Task FindAssetsAsync( - AssetPath assetPath, HashSet remainingChecksumsToFind, Dictionary result, CancellationToken cancellationToken) + AssetPath assetPath, HashSet remainingChecksumsToFind, Action onAssetFound, CancellationToken cancellationToken) { var solutionState = this.CompilationState; @@ -74,13 +77,13 @@ private async Task FindAssetsAsync( // If we're not in a project cone, start the search at the top most state-checksum corresponding to the // entire solution. Contract.ThrowIfFalse(solutionState.TryGetStateChecksums(out var stateChecksums)); - await stateChecksums.FindAsync(solutionState, this.ProjectCone, assetPath, remainingChecksumsToFind, result, cancellationToken).ConfigureAwait(false); + await stateChecksums.FindAsync(solutionState, this.ProjectCone, assetPath, remainingChecksumsToFind, onAssetFound, cancellationToken).ConfigureAwait(false); } else { // Otherwise, grab the top-most state checksum for this cone and search within that. Contract.ThrowIfFalse(solutionState.TryGetStateChecksums(this.ProjectCone.RootProjectId, out var stateChecksums)); - await stateChecksums.FindAsync(solutionState, this.ProjectCone, assetPath, remainingChecksumsToFind, result, cancellationToken).ConfigureAwait(false); + await stateChecksums.FindAsync(solutionState, this.ProjectCone, assetPath, remainingChecksumsToFind, onAssetFound, cancellationToken).ConfigureAwait(false); } } @@ -97,10 +100,10 @@ public async ValueTask GetAssetAsync(Checksum checksum, CancellationToke { Contract.ThrowIfTrue(checksum == Checksum.Null); - using var checksumPool = Creator.CreateChecksumSet(checksum); using var _ = Creator.CreateResultMap(out var resultPool); - await scope.FindAssetsAsync(AssetPath.FullLookupForTesting, checksumPool.Object, resultPool, cancellationToken).ConfigureAwait(false); + var checksums = new ReadOnlyMemory([checksum]); + await scope.AddAssetsAsync(AssetPath.FullLookupForTesting, checksums, resultPool, cancellationToken).ConfigureAwait(false); Contract.ThrowIfTrue(resultPool.Count != 1); var (resultingChecksum, value) = resultPool.First(); diff --git a/src/Workspaces/Remote/ServiceHub/Host/TestUtils.cs b/src/Workspaces/Remote/ServiceHub/Host/TestUtils.cs index 90a03a7481fc5..4363e746ad586 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/TestUtils.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/TestUtils.cs @@ -184,20 +184,22 @@ public static Task AppendAssetMapAsync(this Solution solution, Dictionary map, ProjectId? projectId, CancellationToken cancellationToken) { + var callback = (Checksum checksum, object asset) => { map[checksum] = asset; }; + if (projectId == null) { var compilationChecksums = await solution.CompilationState.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); - await compilationChecksums.FindAsync(solution.CompilationState, projectCone: null, AssetPath.FullLookupForTesting, Flatten(compilationChecksums), map, cancellationToken).ConfigureAwait(false); + await compilationChecksums.FindAsync(solution.CompilationState, projectCone: null, AssetPath.FullLookupForTesting, Flatten(compilationChecksums), callback, cancellationToken).ConfigureAwait(false); foreach (var frozenSourceGeneratedDocumentState in solution.CompilationState.FrozenSourceGeneratedDocumentStates?.States.Values ?? []) { var documentChecksums = await frozenSourceGeneratedDocumentState.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); - await compilationChecksums.FindAsync(solution.CompilationState, projectCone: null, AssetPath.FullLookupForTesting, Flatten(documentChecksums), map, cancellationToken).ConfigureAwait(false); + await compilationChecksums.FindAsync(solution.CompilationState, projectCone: null, AssetPath.FullLookupForTesting, Flatten(documentChecksums), callback, cancellationToken).ConfigureAwait(false); } var solutionChecksums = await solution.CompilationState.SolutionState.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); Contract.ThrowIfTrue(solutionChecksums.ProjectCone != null); - await solutionChecksums.FindAsync(solution.CompilationState.SolutionState, projectCone: null, AssetPath.FullLookupForTesting, Flatten(solutionChecksums), map, cancellationToken).ConfigureAwait(false); + await solutionChecksums.FindAsync(solution.CompilationState.SolutionState, projectCone: null, AssetPath.FullLookupForTesting, Flatten(solutionChecksums), callback, cancellationToken).ConfigureAwait(false); foreach (var project in solution.Projects) await project.AppendAssetMapAsync(map, cancellationToken).ConfigureAwait(false); @@ -205,11 +207,11 @@ public static async Task AppendAssetMapAsync( else { var (compilationChecksums, projectCone) = await solution.CompilationState.GetStateChecksumsAsync(projectId, cancellationToken).ConfigureAwait(false); - await compilationChecksums.FindAsync(solution.CompilationState, projectCone, AssetPath.SolutionAndProjectForTesting(projectId), Flatten(compilationChecksums), map, cancellationToken).ConfigureAwait(false); + await compilationChecksums.FindAsync(solution.CompilationState, projectCone, AssetPath.SolutionAndProjectForTesting(projectId), Flatten(compilationChecksums), callback, cancellationToken).ConfigureAwait(false); var solutionChecksums = await solution.CompilationState.SolutionState.GetStateChecksumsAsync(projectId, cancellationToken).ConfigureAwait(false); Contract.ThrowIfFalse(projectCone.Equals(solutionChecksums.ProjectCone)); - await solutionChecksums.FindAsync(solution.CompilationState.SolutionState, projectCone, AssetPath.SolutionAndProjectForTesting(projectId), Flatten(solutionChecksums), map, cancellationToken).ConfigureAwait(false); + await solutionChecksums.FindAsync(solution.CompilationState.SolutionState, projectCone, AssetPath.SolutionAndProjectForTesting(projectId), Flatten(solutionChecksums), callback, cancellationToken).ConfigureAwait(false); var project = solution.GetRequiredProject(projectId); await project.AppendAssetMapAsync(map, cancellationToken).ConfigureAwait(false); @@ -225,13 +227,15 @@ private static async Task AppendAssetMapAsync(this Project project, Dictionary { map[checksum] = asset; }; + var projectChecksums = await project.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); - await projectChecksums.FindAsync(project.State, AssetPath.FullLookupForTesting, Flatten(projectChecksums), map, cancellationToken).ConfigureAwait(false); + await projectChecksums.FindAsync(project.State, AssetPath.FullLookupForTesting, Flatten(projectChecksums), callback, cancellationToken).ConfigureAwait(false); foreach (var document in project.Documents.Concat(project.AdditionalDocuments).Concat(project.AnalyzerConfigDocuments)) { var documentChecksums = await document.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); - await documentChecksums.FindAsync(AssetPathKind.Documents, document.State, Flatten(documentChecksums), map, cancellationToken).ConfigureAwait(false); + await documentChecksums.FindAsync(AssetPathKind.Documents, document.State, Flatten(documentChecksums), callback, cancellationToken).ConfigureAwait(false); } }