Skip to content

Commit

Permalink
Optimize calculation of remote supported languages
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed Jun 18, 2021
1 parent 6920e1e commit e7ac5ba
Showing 1 changed file with 58 additions and 2 deletions.
60 changes: 58 additions & 2 deletions src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ internal partial class SolutionState
private readonly SolutionInfo.SolutionAttributes _solutionAttributes;
private readonly SolutionServices _solutionServices;
private readonly ImmutableDictionary<ProjectId, ProjectState> _projectIdToProjectStateMap;
private readonly ImmutableHashSet<string> _remoteSupportedLanguages;
private readonly ImmutableDictionary<string, ImmutableArray<DocumentId>> _filePathToDocumentIdsMap;
private readonly ProjectDependencyGraph _dependencyGraph;

Expand Down Expand Up @@ -74,6 +75,7 @@ private SolutionState(
SerializableOptionSet options,
IReadOnlyList<AnalyzerReference> analyzerReferences,
ImmutableDictionary<ProjectId, ProjectState> idToProjectStateMap,
ImmutableHashSet<string> remoteSupportedLanguages,
ImmutableDictionary<ProjectId, ICompilationTracker> projectIdToTrackerMap,
ImmutableDictionary<string, ImmutableArray<DocumentId>> filePathToDocumentIdsMap,
ProjectDependencyGraph dependencyGraph,
Expand All @@ -88,6 +90,7 @@ private SolutionState(
Options = options;
AnalyzerReferences = analyzerReferences;
_projectIdToProjectStateMap = idToProjectStateMap;
_remoteSupportedLanguages = remoteSupportedLanguages;
_projectIdToTrackerMap = projectIdToTrackerMap;
_filePathToDocumentIdsMap = filePathToDocumentIdsMap;
_dependencyGraph = dependencyGraph;
Expand Down Expand Up @@ -119,6 +122,7 @@ public SolutionState(
options,
analyzerReferences,
idToProjectStateMap: ImmutableDictionary<ProjectId, ProjectState>.Empty,
remoteSupportedLanguages: ImmutableHashSet<string>.Empty,
projectIdToTrackerMap: ImmutableDictionary<ProjectId, ICompilationTracker>.Empty,
filePathToDocumentIdsMap: ImmutableDictionary.Create<string, ImmutableArray<DocumentId>>(StringComparer.OrdinalIgnoreCase),
dependencyGraph: ProjectDependencyGraph.Empty,
Expand Down Expand Up @@ -203,6 +207,8 @@ private void CheckInvariants()
// project ids must be the same:
Debug.Assert(_projectIdToProjectStateMap.Keys.SetEquals(ProjectIds));
Debug.Assert(_projectIdToProjectStateMap.Keys.SetEquals(_dependencyGraph.ProjectIds));

Debug.Assert(_remoteSupportedLanguages.SetEquals(GetRemoteSupportedProjectLanguages(_projectIdToProjectStateMap)));
}

private SolutionState Branch(
Expand All @@ -211,17 +217,25 @@ private SolutionState Branch(
SerializableOptionSet? options = null,
IReadOnlyList<AnalyzerReference>? analyzerReferences = null,
ImmutableDictionary<ProjectId, ProjectState>? idToProjectStateMap = null,
ImmutableHashSet<string>? remoteSupportedProjectLanguages = null,
ImmutableDictionary<ProjectId, ICompilationTracker>? projectIdToTrackerMap = null,
ImmutableDictionary<string, ImmutableArray<DocumentId>>? filePathToDocumentIdsMap = null,
ProjectDependencyGraph? dependencyGraph = null,
Optional<SourceGeneratedDocumentState?> frozenSourceGeneratedDocument = default)
{
var branchId = GetBranchId();

if (idToProjectStateMap is not null)
{
Contract.ThrowIfNull(remoteSupportedProjectLanguages);
}

solutionAttributes ??= _solutionAttributes;
projectIds ??= ProjectIds;
idToProjectStateMap ??= _projectIdToProjectStateMap;
options ??= Options.WithLanguages(GetRemoteSupportedProjectLanguages(idToProjectStateMap));
remoteSupportedProjectLanguages ??= _remoteSupportedLanguages;
Debug.Assert(remoteSupportedProjectLanguages.SetEquals(GetRemoteSupportedProjectLanguages(idToProjectStateMap)));
options ??= Options.WithLanguages(remoteSupportedProjectLanguages);
analyzerReferences ??= AnalyzerReferences;
projectIdToTrackerMap ??= _projectIdToTrackerMap;
filePathToDocumentIdsMap ??= _filePathToDocumentIdsMap;
Expand Down Expand Up @@ -253,6 +267,7 @@ private SolutionState Branch(
options,
analyzerReferences,
idToProjectStateMap,
remoteSupportedProjectLanguages,
projectIdToTrackerMap,
filePathToDocumentIdsMap,
dependencyGraph,
Expand Down Expand Up @@ -281,6 +296,7 @@ private SolutionState CreatePrimarySolution(
Options,
AnalyzerReferences,
_projectIdToProjectStateMap,
_remoteSupportedLanguages,
_projectIdToTrackerMap,
_filePathToDocumentIdsMap,
_dependencyGraph,
Expand Down Expand Up @@ -461,6 +477,9 @@ private SolutionState AddProject(ProjectId projectId, ProjectState projectState)

var newProjectIds = ProjectIds.ToImmutableArray().Add(projectId);
var newStateMap = _projectIdToProjectStateMap.Add(projectId, projectState);
var newLanguages = RemoteSupportedLanguages.IsSupported(projectState.Language)
? _remoteSupportedLanguages.Add(projectState.Language)
: _remoteSupportedLanguages;

var newDependencyGraph = _dependencyGraph
.WithAdditionalProject(projectId)
Expand Down Expand Up @@ -490,6 +509,7 @@ private SolutionState AddProject(ProjectId projectId, ProjectState projectState)
solutionAttributes: newSolutionAttributes,
projectIds: newProjectIds,
idToProjectStateMap: newStateMap,
remoteSupportedProjectLanguages: newLanguages,
projectIdToTrackerMap: newTrackerMap,
filePathToDocumentIdsMap: newFilePathToDocumentIdsMap,
dependencyGraph: newDependencyGraph);
Expand Down Expand Up @@ -573,6 +593,31 @@ public SolutionState RemoveProject(ProjectId projectId)

var newProjectIds = ProjectIds.ToImmutableArray().Remove(projectId);
var newStateMap = _projectIdToProjectStateMap.Remove(projectId);

// Remote supported languages only changes if the removed project is the last project of a supported language.
var newLanguages = _remoteSupportedLanguages;
if (_projectIdToProjectStateMap.TryGetValue(projectId, out var projectState)
&& RemoteSupportedLanguages.IsSupported(projectState.Language))
{
var stillSupportsLanguage = false;
foreach (var (id, state) in _projectIdToProjectStateMap)
{
if (id == projectId)
continue;

if (state.Language == projectState.Language)
{
stillSupportsLanguage = true;
break;
}
}

if (!stillSupportsLanguage)
{
newLanguages = newLanguages.Remove(projectState.Language);
}
}

var newDependencyGraph = _dependencyGraph.WithProjectRemoved(projectId);
var newTrackerMap = CreateCompilationTrackerMap(projectId, newDependencyGraph);
var newFilePathToDocumentIdsMap = CreateFilePathToDocumentIdsMapWithRemovedDocuments(GetDocumentStates(_projectIdToProjectStateMap[projectId]));
Expand All @@ -581,6 +626,7 @@ public SolutionState RemoveProject(ProjectId projectId)
solutionAttributes: newSolutionAttributes,
projectIds: newProjectIds,
idToProjectStateMap: newStateMap,
remoteSupportedProjectLanguages: newLanguages,
projectIdToTrackerMap: newTrackerMap.Remove(projectId),
filePathToDocumentIdsMap: newFilePathToDocumentIdsMap,
dependencyGraph: newDependencyGraph);
Expand Down Expand Up @@ -1468,6 +1514,13 @@ private SolutionState ForkProject(
var projectId = newProjectState.Id;

var newStateMap = _projectIdToProjectStateMap.SetItem(projectId, newProjectState);

// Remote supported languages can only change if the project changes language. This is an unexpected edge
// case, so it's not optimized for incremental updates.
var newLanguages = !_projectIdToProjectStateMap.TryGetValue(projectId, out var projectState) || projectState.Language != newProjectState.Language
? GetRemoteSupportedProjectLanguages(newStateMap)
: _remoteSupportedLanguages;

newDependencyGraph ??= _dependencyGraph;
var newTrackerMap = CreateCompilationTrackerMap(projectId, newDependencyGraph);
// If we have a tracker for this project, then fork it as well (along with the
Expand All @@ -1484,6 +1537,7 @@ private SolutionState ForkProject(

return this.Branch(
idToProjectStateMap: newStateMap,
remoteSupportedProjectLanguages: newLanguages,
projectIdToTrackerMap: newTrackerMap,
dependencyGraph: newDependencyGraph,
filePathToDocumentIdsMap: newFilePathToDocumentIdsMap ?? _filePathToDocumentIdsMap);
Expand Down Expand Up @@ -1671,10 +1725,12 @@ public SolutionState WithFrozenPartialCompilationIncludingSpecificDocument(Docum
var newTracker = tracker.FreezePartialStateWithTree(this, doc, tree, cancellationToken);

var newIdToProjectStateMap = _projectIdToProjectStateMap.SetItem(documentId.ProjectId, newTracker.ProjectState);
var newLanguages = _remoteSupportedLanguages;
var newIdToTrackerMap = _projectIdToTrackerMap.SetItem(documentId.ProjectId, newTracker);

currentPartialSolution = this.Branch(
idToProjectStateMap: newIdToProjectStateMap,
remoteSupportedProjectLanguages: newLanguages,
projectIdToTrackerMap: newIdToTrackerMap,
dependencyGraph: CreateDependencyGraph(ProjectIds, newIdToProjectStateMap));

Expand Down Expand Up @@ -2002,7 +2058,7 @@ internal bool ContainsTransitiveReference(ProjectId fromProjectId, ProjectId toP
=> _dependencyGraph.GetProjectsThatThisProjectTransitivelyDependsOn(fromProjectId).Contains(toProjectId);

internal ImmutableHashSet<string> GetRemoteSupportedProjectLanguages()
=> GetRemoteSupportedProjectLanguages(ProjectStates);
=> _remoteSupportedLanguages;

private static ImmutableHashSet<string> GetRemoteSupportedProjectLanguages(ImmutableDictionary<ProjectId, ProjectState> projectStates)
{
Expand Down

0 comments on commit e7ac5ba

Please sign in to comment.