Skip to content

Commit

Permalink
Avoid eagerly realizing the declaration table
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed Nov 20, 2021
1 parent 98cf30b commit c154315
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2589,7 +2589,7 @@ internal DeclarationTable Declarations
{
get
{
return _syntaxAndDeclarations.GetLazyState().DeclarationTable;
return _syntaxAndDeclarations.GetLazyState().DeclarationTableInput.GetDeclarationTable();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp
Expand All @@ -22,15 +20,15 @@ internal sealed class State
internal readonly ImmutableDictionary<SyntaxTree, ImmutableArray<LoadDirective>> LoadDirectiveMap;
internal readonly ImmutableDictionary<string, SyntaxTree> LoadedSyntaxTreeMap;
internal readonly ImmutableDictionary<SyntaxTree, Lazy<RootSingleNamespaceDeclaration>> RootNamespaces;
internal readonly DeclarationTable DeclarationTable;
internal readonly DeclarationTableInput DeclarationTableInput;

internal State(
ImmutableArray<SyntaxTree> syntaxTrees,
ImmutableDictionary<SyntaxTree, int> syntaxTreeOrdinalMap,
ImmutableDictionary<SyntaxTree, ImmutableArray<LoadDirective>> loadDirectiveMap,
ImmutableDictionary<string, SyntaxTree> loadedSyntaxTreeMap,
ImmutableDictionary<SyntaxTree, Lazy<RootSingleNamespaceDeclaration>> rootNamespaces,
DeclarationTable declarationTable)
DeclarationTableInput declarationTableInput)
{
Debug.Assert(syntaxTrees.All(tree => syntaxTrees[syntaxTreeOrdinalMap[tree]] == tree));
Debug.Assert(syntaxTrees.SetEquals(rootNamespaces.Keys.AsImmutable(), EqualityComparer<SyntaxTree>.Default));
Expand All @@ -40,7 +38,7 @@ internal State(
this.LoadDirectiveMap = loadDirectiveMap;
this.LoadedSyntaxTreeMap = loadedSyntaxTreeMap;
this.RootNamespaces = rootNamespaces;
this.DeclarationTable = declarationTable;
this.DeclarationTableInput = declarationTableInput;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private static State CreateState(
var loadDirectiveMapBuilder = PooledDictionary<SyntaxTree, ImmutableArray<LoadDirective>>.GetInstance();
var loadedSyntaxTreeMapBuilder = PooledDictionary<string, SyntaxTree>.GetInstance();
var declMapBuilder = PooledDictionary<SyntaxTree, Lazy<RootSingleNamespaceDeclaration>>.GetInstance();
var declTable = DeclarationTable.Empty;
var declTableInput = DeclarationTableInput.Empty;

foreach (var tree in externalSyntaxTrees)
{
Expand All @@ -68,7 +68,7 @@ private static State CreateState(
loadDirectiveMapBuilder,
loadedSyntaxTreeMapBuilder,
declMapBuilder,
ref declTable);
ref declTableInput);
}

return new State(
Expand All @@ -77,7 +77,7 @@ private static State CreateState(
loadDirectiveMapBuilder.ToImmutableDictionaryAndFree(),
loadedSyntaxTreeMapBuilder.ToImmutableDictionaryAndFree(),
declMapBuilder.ToImmutableDictionaryAndFree(),
declTable);
declTableInput);
}

public SyntaxAndDeclarationManager AddSyntaxTrees(IEnumerable<SyntaxTree> trees)
Expand All @@ -98,7 +98,7 @@ public SyntaxAndDeclarationManager AddSyntaxTrees(IEnumerable<SyntaxTree> trees)
var loadDirectiveMapBuilder = state.LoadDirectiveMap.ToBuilder();
var loadedSyntaxTreeMapBuilder = state.LoadedSyntaxTreeMap.ToBuilder();
var declMapBuilder = state.RootNamespaces.ToBuilder();
var declTable = state.DeclarationTable;
var declTableInput = state.DeclarationTableInput.Reduce();

var treesBuilder = ArrayBuilder<SyntaxTree>.GetInstance();
treesBuilder.AddRange(state.SyntaxTrees);
Expand All @@ -116,7 +116,7 @@ public SyntaxAndDeclarationManager AddSyntaxTrees(IEnumerable<SyntaxTree> trees)
loadDirectiveMapBuilder,
loadedSyntaxTreeMapBuilder,
declMapBuilder,
ref declTable);
ref declTableInput);
}

state = new State(
Expand All @@ -125,7 +125,7 @@ public SyntaxAndDeclarationManager AddSyntaxTrees(IEnumerable<SyntaxTree> trees)
loadDirectiveMapBuilder.ToImmutableDictionary(),
loadedSyntaxTreeMapBuilder.ToImmutableDictionary(),
declMapBuilder.ToImmutableDictionary(),
declTable);
declTableInput);

return new SyntaxAndDeclarationManager(
newExternalSyntaxTrees,
Expand All @@ -150,15 +150,15 @@ private static void AppendAllSyntaxTrees(
IDictionary<SyntaxTree, ImmutableArray<LoadDirective>> loadDirectiveMapBuilder,
IDictionary<string, SyntaxTree> loadedSyntaxTreeMapBuilder,
IDictionary<SyntaxTree, Lazy<RootSingleNamespaceDeclaration>> declMapBuilder,
ref DeclarationTable declTable)
ref DeclarationTableInput declTableInput)
{
var sourceCodeKind = tree.Options.Kind;
if (sourceCodeKind == SourceCodeKind.Script)
{
AppendAllLoadedSyntaxTrees(treesBuilder, tree, scriptClassName, resolver, messageProvider, isSubmission, ordinalMapBuilder, loadDirectiveMapBuilder, loadedSyntaxTreeMapBuilder, declMapBuilder, ref declTable);
AppendAllLoadedSyntaxTrees(treesBuilder, tree, scriptClassName, resolver, messageProvider, isSubmission, ordinalMapBuilder, loadDirectiveMapBuilder, loadedSyntaxTreeMapBuilder, declMapBuilder, ref declTableInput);
}

AddSyntaxTreeToDeclarationMapAndTable(tree, scriptClassName, isSubmission, declMapBuilder, ref declTable);
AddSyntaxTreeToDeclarationMapAndTable(tree, scriptClassName, isSubmission, declMapBuilder, ref declTableInput);

treesBuilder.Add(tree);

Expand All @@ -176,7 +176,7 @@ private static void AppendAllLoadedSyntaxTrees(
IDictionary<SyntaxTree, ImmutableArray<LoadDirective>> loadDirectiveMapBuilder,
IDictionary<string, SyntaxTree> loadedSyntaxTreeMapBuilder,
IDictionary<SyntaxTree, Lazy<RootSingleNamespaceDeclaration>> declMapBuilder,
ref DeclarationTable declTable)
ref DeclarationTableInput declTableInput)
{
ArrayBuilder<LoadDirective> loadDirectives = null;

Expand Down Expand Up @@ -236,7 +236,7 @@ private static void AppendAllLoadedSyntaxTrees(
loadDirectiveMapBuilder,
loadedSyntaxTreeMapBuilder,
declMapBuilder,
ref declTable);
ref declTableInput);
}
catch (Exception e)
{
Expand Down Expand Up @@ -271,11 +271,11 @@ private static void AddSyntaxTreeToDeclarationMapAndTable(
string scriptClassName,
bool isSubmission,
IDictionary<SyntaxTree, Lazy<RootSingleNamespaceDeclaration>> declMapBuilder,
ref DeclarationTable declTable)
ref DeclarationTableInput declTableInput)
{
var lazyRoot = new Lazy<RootSingleNamespaceDeclaration>(() => DeclarationTreeBuilder.ForTree(tree, scriptClassName, isSubmission));
declMapBuilder.Add(tree, lazyRoot); // Callers are responsible for checking for existing entries.
declTable = declTable.AddRootDeclaration(lazyRoot);
declTableInput = declTableInput.AddRootDeclaration(lazyRoot);
}

public SyntaxAndDeclarationManager RemoveSyntaxTrees(HashSet<SyntaxTree> trees)
Expand Down Expand Up @@ -310,14 +310,14 @@ public SyntaxAndDeclarationManager RemoveSyntaxTrees(HashSet<SyntaxTree> trees)
var treesBuilder = ArrayBuilder<SyntaxTree>.GetInstance();
var ordinalMapBuilder = PooledDictionary<SyntaxTree, int>.GetInstance();
var declMapBuilder = state.RootNamespaces.ToBuilder();
var declTable = state.DeclarationTable;
var declTableInput = state.DeclarationTableInput.Reduce();
foreach (var tree in syntaxTrees)
{
if (removeSet.Contains(tree))
{
loadDirectiveMap = loadDirectiveMap.Remove(tree);
loadedSyntaxTreeMap = loadedSyntaxTreeMap.Remove(tree.FilePath);
RemoveSyntaxTreeFromDeclarationMapAndTable(tree, declMapBuilder, ref declTable);
RemoveSyntaxTreeFromDeclarationMapAndTable(tree, declMapBuilder, ref declTableInput);
}
else if (!IsLoadedSyntaxTree(tree, loadedSyntaxTreeMap))
{
Expand All @@ -337,7 +337,7 @@ public SyntaxAndDeclarationManager RemoveSyntaxTrees(HashSet<SyntaxTree> trees)
loadDirectiveMap,
loadedSyntaxTreeMap,
declMapBuilder.ToImmutableDictionary(),
declTable);
declTableInput);

return new SyntaxAndDeclarationManager(
newExternalSyntaxTrees,
Expand Down Expand Up @@ -430,10 +430,10 @@ private static void GetRemoveSetForLoadedTrees(
private static void RemoveSyntaxTreeFromDeclarationMapAndTable(
SyntaxTree tree,
IDictionary<SyntaxTree, Lazy<RootSingleNamespaceDeclaration>> declMap,
ref DeclarationTable declTable)
ref DeclarationTableInput declTableInput)
{
var lazyRoot = declMap[tree];
declTable = declTable.RemoveRootDeclaration(lazyRoot);
declTableInput = declTableInput.RemoveRootDeclaration(lazyRoot);
declMap.Remove(tree);
}

Expand Down Expand Up @@ -469,12 +469,12 @@ public SyntaxAndDeclarationManager ReplaceSyntaxTree(SyntaxTree oldTree, SyntaxT
var loadDirectiveMapBuilder = loadDirectiveMap.ToBuilder();
var loadedSyntaxTreeMapBuilder = loadedSyntaxTreeMap.ToBuilder();
var declMapBuilder = state.RootNamespaces.ToBuilder();
var declTable = state.DeclarationTable;
var declTableInput = state.DeclarationTableInput.Reduce();
foreach (var tree in removeSet)
{
loadDirectiveMapBuilder.Remove(tree);
loadedSyntaxTreeMapBuilder.Remove(tree.FilePath);
RemoveSyntaxTreeFromDeclarationMapAndTable(tree, declMapBuilder, ref declTable);
RemoveSyntaxTreeFromDeclarationMapAndTable(tree, declMapBuilder, ref declTableInput);
}
removeSet.Free();

Expand Down Expand Up @@ -508,7 +508,7 @@ public SyntaxAndDeclarationManager ReplaceSyntaxTree(SyntaxTree oldTree, SyntaxT
loadDirectiveMapBuilder,
loadedSyntaxTreeMapBuilder,
declMapBuilder,
ref declTable);
ref declTableInput);

for (var i = oldOrdinal + 1; i < syntaxTrees.Length; i++)
{
Expand All @@ -530,7 +530,7 @@ public SyntaxAndDeclarationManager ReplaceSyntaxTree(SyntaxTree oldTree, SyntaxT
}
else
{
AddSyntaxTreeToDeclarationMapAndTable(newTree, this.ScriptClassName, this.IsSubmission, declMapBuilder, ref declTable);
AddSyntaxTreeToDeclarationMapAndTable(newTree, this.ScriptClassName, this.IsSubmission, declMapBuilder, ref declTableInput);

if (newLoadDirectivesSyntax.Any())
{
Expand All @@ -556,7 +556,7 @@ public SyntaxAndDeclarationManager ReplaceSyntaxTree(SyntaxTree oldTree, SyntaxT
loadDirectiveMapBuilder.ToImmutable(),
loadedSyntaxTreeMapBuilder.ToImmutable(),
declMapBuilder.ToImmutable(),
declTable);
declTableInput);

return new SyntaxAndDeclarationManager(
newExternalSyntaxTrees,
Expand Down Expand Up @@ -629,7 +629,7 @@ internal bool MayHaveReferenceDirectives()
return externalSyntaxTrees.Any(t => t.HasReferenceOrLoadDirectives());
}

return state.DeclarationTable.ReferenceDirectives.Any();
return state.DeclarationTableInput.GetDeclarationTable().ReferenceDirectives.Any();
}

private static bool TryGetLoadedSyntaxTree(ImmutableDictionary<string, SyntaxTree> loadedSyntaxTreeMap, LoadDirective directive, out SyntaxTree loadedTree)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// 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.Immutable;
using System.Threading;

namespace Microsoft.CodeAnalysis.CSharp
{
internal sealed class DeclarationTableInput
{
public static readonly DeclarationTableInput Empty = new(DeclarationTable.Empty, ImmutableList<(bool isAdd, Lazy<RootSingleNamespaceDeclaration> lazyRoot)>.Empty);

private readonly DeclarationTable _initialTable;
private readonly ImmutableList<(bool isAdd, Lazy<RootSingleNamespaceDeclaration> lazyRoot)> _instructions;
private DeclarationTable? _finalTable;

private DeclarationTableInput(DeclarationTable initialTable, ImmutableList<(bool isAdd, Lazy<RootSingleNamespaceDeclaration> lazyRoot)> instructions)
{
_initialTable = initialTable;
_instructions = instructions;
}

internal DeclarationTableInput AddRootDeclaration(Lazy<RootSingleNamespaceDeclaration> lazyRoot)
{
var initialTable = _finalTable;
var baseInstructions = ImmutableList<(bool isAdd, Lazy<RootSingleNamespaceDeclaration> lazyRoot)>.Empty;
if (initialTable is null)
{
initialTable = _initialTable;
baseInstructions = _instructions;
}

var instructions = baseInstructions.Add((isAdd: true, lazyRoot));
return new DeclarationTableInput(initialTable, instructions);
}

internal DeclarationTableInput RemoveRootDeclaration(Lazy<RootSingleNamespaceDeclaration> lazyRoot)
{
var initialTable = _finalTable;
var baseInstructions = ImmutableList<(bool isAdd, Lazy<RootSingleNamespaceDeclaration> lazyRoot)>.Empty;
if (initialTable is null)
{
initialTable = _initialTable;
baseInstructions = _instructions;
}

var instructions = baseInstructions.Add((isAdd: false, lazyRoot));
return new DeclarationTableInput(initialTable, instructions);
}

internal DeclarationTableInput Reduce()
{
if (this is { _instructions.IsEmpty: false, _finalTable: { } finalTable })
{
return new DeclarationTableInput(finalTable, ImmutableList<(bool isAdd, Lazy<RootSingleNamespaceDeclaration> lazyRoot)>.Empty);
}

return this;
}

internal DeclarationTable GetDeclarationTable()
{
if (_finalTable is null)
{
var table = _initialTable;
foreach (var (isAdd, lazyRoot) in _instructions)
{
if (isAdd)
table = table.AddRootDeclaration(lazyRoot);
else
table = table.RemoveRootDeclaration(lazyRoot);
}

Interlocked.CompareExchange(ref _finalTable, table, null);
}

return _finalTable;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -408,12 +408,12 @@ public async Task ChangeToDocumentThatDoesNotImpactGeneratedDocumentReusesDeclar

Assert.Same(cachedStateAfterFirstChange, cachedStateAfterSecondChange);

static object GetDeclarationManagerCachedStateForUnchangingTrees(Compilation compilation)
static object? GetDeclarationManagerCachedStateForUnchangingTrees(Compilation compilation)
{
var syntaxAndDeclarationsManager = compilation.GetFieldValue("_syntaxAndDeclarations");
var state = syntaxAndDeclarationsManager.GetType().GetMethod("GetLazyState", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!.Invoke(syntaxAndDeclarationsManager, null);
var declarationTable = state.GetFieldValue("DeclarationTable");
return declarationTable.GetFieldValue("_cache");
var declarationTable = state.GetFieldValue("DeclarationTableInput")?.GetFieldValue("_finalTable");
return declarationTable?.GetFieldValue("_cache");
}

static async Task<Project> MakeChangesToDocument(Project project)
Expand Down

0 comments on commit c154315

Please sign in to comment.