Skip to content

Commit

Permalink
Merge pull request #72403 from CyrusNajmabadi/skeletonCache
Browse files Browse the repository at this point in the history
  • Loading branch information
CyrusNajmabadi authored Mar 7, 2024
2 parents 8c55b1f + c763cb2 commit d683273
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ private partial class CompilationTracker : ICompilationTracker
// guarantees only one thread is building at a time
private SemaphoreSlim? _buildLock;

public SkeletonReferenceCache SkeletonReferenceCache { get; }
/// <summary>
/// Intentionally not readonly. This is a mutable struct.
/// </summary>
private SkeletonReferenceCache _skeletonReferenceCache;

/// <summary>
/// Set via a feature flag to enable strict validation of the compilations that are produced, in that they match the original states. This validation is expensive, so we don't want it
Expand All @@ -55,13 +58,14 @@ private partial class CompilationTracker : ICompilationTracker
private CompilationTracker(
ProjectState project,
CompilationTrackerState? state,
SkeletonReferenceCache cachedSkeletonReferences)
in SkeletonReferenceCache skeletonReferenceCacheToClone)
{
Contract.ThrowIfNull(project);

this.ProjectState = project;
_stateDoNotAccessDirectly = state;
this.SkeletonReferenceCache = cachedSkeletonReferences;

_skeletonReferenceCache = skeletonReferenceCacheToClone.Clone();

_validateStates = project.LanguageServices.SolutionServices.GetRequiredService<IWorkspaceConfigurationService>().Options.ValidateCompilationTrackerStates;

Expand All @@ -73,7 +77,7 @@ private CompilationTracker(
/// and will have no extra information beyond the project itself.
/// </summary>
public CompilationTracker(ProjectState project)
: this(project, state: null, cachedSkeletonReferences: new())
: this(project, state: null, skeletonReferenceCacheToClone: new())
{
}

Expand Down Expand Up @@ -131,7 +135,7 @@ public ICompilationTracker Fork(
return new CompilationTracker(
newProjectState,
forkedTrackerState,
this.SkeletonReferenceCache.Clone());
skeletonReferenceCacheToClone: _skeletonReferenceCache);

CompilationTrackerState? ForkTrackerState()
{
Expand Down Expand Up @@ -656,7 +660,7 @@ private Compilation CreateEmptyCompilation()
// generate on demand. So just try to see if we can grab the last generated skeleton for that
// project.
var properties = new MetadataReferenceProperties(aliases: projectReference.Aliases, embedInteropTypes: projectReference.EmbedInteropTypes);
return this.SkeletonReferenceCache.TryGetAlreadyBuiltMetadataReference(properties);
return _skeletonReferenceCache.TryGetAlreadyBuiltMetadataReference(properties);
}

return null;
Expand All @@ -682,14 +686,13 @@ public ICompilationTracker FreezePartialState(CancellationToken cancellationToke
{
var state = this.ReadState();

var clonedCache = this.SkeletonReferenceCache.Clone();
if (state is FinalCompilationTrackerState finalState)
{
// If we're finalized and already frozen, we can just use ourselves. Otherwise, flip the frozen bit
// so that any future forks keep things frozen.
return finalState.IsFrozen
? this
: new CompilationTracker(this.ProjectState, finalState.WithIsFrozen(), clonedCache);
: new CompilationTracker(this.ProjectState, finalState.WithIsFrozen(), skeletonReferenceCacheToClone: _skeletonReferenceCache);
}

// Non-final state currently. Produce an in-progress-state containing the forked change. Note: we
Expand Down Expand Up @@ -743,7 +746,7 @@ public ICompilationTracker FreezePartialState(CancellationToken cancellationToke
CompilationTrackerGeneratorInfo.Empty,
lazyCompilationWithGeneratedDocuments,
pendingTranslationActions: []),
clonedCache);
skeletonReferenceCacheToClone: _skeletonReferenceCache);
}
else if (state is InProgressState inProgressState)
{
Expand All @@ -769,7 +772,7 @@ public ICompilationTracker FreezePartialState(CancellationToken cancellationToke
generatorInfo,
compilationWithGeneratedDocuments,
pendingTranslationActions: []),
clonedCache);
skeletonReferenceCacheToClone: _skeletonReferenceCache);
}
else
{
Expand Down Expand Up @@ -858,6 +861,12 @@ private bool IsGeneratorRunResultToIgnore(GeneratorRunResult result)
"Microsoft.CodeAnalysis.Razor.Compiler";
}

public SkeletonReferenceCache GetClonedSkeletonReferenceCache()
=> _skeletonReferenceCache.Clone();

public Task<MetadataReference?> GetOrBuildSkeletonReferenceAsync(SolutionCompilationState compilationState, MetadataReferenceProperties properties, CancellationToken cancellationToken)
=> _skeletonReferenceCache.GetOrBuildReferenceAsync(this, compilationState, properties, cancellationToken);

// END HACK HACK HACK HACK, or the setup of it at least; once this hack is removed the calls to IsGeneratorRunResultToIgnore
// need to be cleaned up.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis;
Expand All @@ -19,8 +18,10 @@ internal partial class SolutionCompilationState
/// to return a generated document with a specific content, regardless of what the generator actually produces. In other words, it says
/// "take the compilation this other thing produced, and pretend the generator gave this content, even if it wouldn't."
/// </summary>
private class GeneratedFileReplacingCompilationTracker(ICompilationTracker underlyingTracker, TextDocumentStates<SourceGeneratedDocumentState> replacementDocumentStates) : ICompilationTracker
private class GeneratedFileReplacingCompilationTracker : ICompilationTracker
{
private readonly TextDocumentStates<SourceGeneratedDocumentState> _replacementDocumentStates;

private AsyncLazy<Checksum>? _lazyDependentChecksum;

/// <summary>
Expand All @@ -30,12 +31,25 @@ private class GeneratedFileReplacingCompilationTracker(ICompilationTracker under
[DisallowNull]
private Compilation? _compilationWithReplacements;

public ICompilationTracker UnderlyingTracker { get; } = underlyingTracker;
public SkeletonReferenceCache SkeletonReferenceCache { get; } = underlyingTracker.SkeletonReferenceCache.Clone();
public ICompilationTracker UnderlyingTracker { get; }
public ProjectState ProjectState => UnderlyingTracker.ProjectState;

public GeneratorDriver? GeneratorDriver => UnderlyingTracker.GeneratorDriver;

/// <summary>
/// Intentionally not readonly as this is a mutable struct.
/// </summary>
private SkeletonReferenceCache _skeletonReferenceCache;

public GeneratedFileReplacingCompilationTracker(
ICompilationTracker underlyingTracker,
TextDocumentStates<SourceGeneratedDocumentState> replacementDocumentStates)
{
this.UnderlyingTracker = underlyingTracker;
_replacementDocumentStates = replacementDocumentStates;
_skeletonReferenceCache = underlyingTracker.GetClonedSkeletonReferenceCache();
}

public bool ContainsAssemblyOrModuleOrDynamic(ISymbol symbol, bool primary, out MetadataReferenceInfo? referencedThrough)
{
if (_compilationWithReplacements == null)
Expand All @@ -61,7 +75,7 @@ public ICompilationTracker Fork(ProjectState newProject, TranslationAction? tran
public ICompilationTracker FreezePartialState(CancellationToken cancellationToken)
{
// Ensure the underlying tracker is totally frozen, and then ensure our replaced generated doc is present.
return new GeneratedFileReplacingCompilationTracker(UnderlyingTracker.FreezePartialState(cancellationToken), replacementDocumentStates);
return new GeneratedFileReplacingCompilationTracker(UnderlyingTracker.FreezePartialState(cancellationToken), _replacementDocumentStates);
}

public async Task<Compilation> GetCompilationAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken)
Expand All @@ -75,7 +89,7 @@ public async Task<Compilation> GetCompilationAsync(SolutionCompilationState comp
var underlyingSourceGeneratedDocuments = await UnderlyingTracker.GetSourceGeneratedDocumentStatesAsync(compilationState, cancellationToken).ConfigureAwait(false);
var newCompilation = await UnderlyingTracker.GetCompilationAsync(compilationState, cancellationToken).ConfigureAwait(false);

foreach (var (id, replacementState) in replacementDocumentStates.States)
foreach (var (id, replacementState) in _replacementDocumentStates.States)
{
underlyingSourceGeneratedDocuments.TryGetState(id, out var existingState);

Expand Down Expand Up @@ -124,7 +138,7 @@ public Task<Checksum> GetDependentChecksumAsync(SolutionCompilationState compila
private async Task<Checksum> ComputeDependentChecksumAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken)
=> Checksum.Create(
await UnderlyingTracker.GetDependentChecksumAsync(compilationState, cancellationToken).ConfigureAwait(false),
(await replacementDocumentStates.GetChecksumsAndIdsAsync(cancellationToken).ConfigureAwait(false)).Checksum);
(await _replacementDocumentStates.GetChecksumsAndIdsAsync(cancellationToken).ConfigureAwait(false)).Checksum);

public MetadataReference? GetPartialMetadataReference(ProjectState fromProject, ProjectReference projectReference)
{
Expand All @@ -144,7 +158,7 @@ public async ValueTask<TextDocumentStates<SourceGeneratedDocumentState>> GetSour
var newStates = await UnderlyingTracker.GetSourceGeneratedDocumentStatesAsync(
compilationState, cancellationToken).ConfigureAwait(false);

foreach (var (id, replacementState) in replacementDocumentStates.States)
foreach (var (id, replacementState) in _replacementDocumentStates.States)
{
if (newStates.Contains(id))
{
Expand Down Expand Up @@ -179,7 +193,7 @@ public bool TryGetCompilation([NotNullWhen(true)] out Compilation? compilation)

public SourceGeneratedDocumentState? TryGetSourceGeneratedDocumentStateForAlreadyGeneratedId(DocumentId documentId)
{
if (replacementDocumentStates.TryGetState(documentId, out var replacementState))
if (_replacementDocumentStates.TryGetState(documentId, out var replacementState))
{
return replacementState;
}
Expand All @@ -198,5 +212,11 @@ public ValueTask<ImmutableArray<Diagnostic>> GetSourceGeneratorDiagnosticsAsync(
// outputs of each other.
return UnderlyingTracker.GetSourceGeneratorDiagnosticsAsync(compilationState, cancellationToken);
}

public SkeletonReferenceCache GetClonedSkeletonReferenceCache()
=> _skeletonReferenceCache.Clone();

public Task<MetadataReference?> GetOrBuildSkeletonReferenceAsync(SolutionCompilationState compilationState, MetadataReferenceProperties properties, CancellationToken cancellationToken)
=> _skeletonReferenceCache.GetOrBuildReferenceAsync(this, compilationState, properties, cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;

namespace Microsoft.CodeAnalysis;

Expand All @@ -17,8 +16,6 @@ private interface ICompilationTracker
ProjectState ProjectState { get; }
GeneratorDriver? GeneratorDriver { get; }

SkeletonReferenceCache SkeletonReferenceCache { get; }

/// <summary>
/// Returns <see langword="true"/> if this <see cref="Project"/>/<see cref="Compilation"/> could produce the
/// given <paramref name="symbol"/>. The symbol must be a <see cref="IAssemblySymbol"/>, <see
Expand Down Expand Up @@ -52,5 +49,8 @@ private interface ICompilationTracker
Task<bool> HasSuccessfullyLoadedAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken);
bool TryGetCompilation([NotNullWhen(true)] out Compilation? compilation);
SourceGeneratedDocumentState? TryGetSourceGeneratedDocumentStateForAlreadyGeneratedId(DocumentId documentId);

SkeletonReferenceCache GetClonedSkeletonReferenceCache();
Task<MetadataReference?> GetOrBuildSkeletonReferenceAsync(SolutionCompilationState compilationState, MetadataReferenceProperties properties, CancellationToken cancellationToken);
}
}
Loading

0 comments on commit d683273

Please sign in to comment.