-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reapply "Load analyzers and generators in isolated ALCs in our OOP pr…
…ocess" This reverts commit a59061f. Directories Downstream
- Loading branch information
1 parent
80d8d73
commit 846afc9
Showing
23 changed files
with
767 additions
and
130 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
114 changes: 114 additions & 0 deletions
114
src/Workspaces/Core/Portable/Workspace/IsolatedAnalyzerFileReference.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
// 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. | ||
|
||
#if NET | ||
|
||
using System; | ||
using System.Collections.Immutable; | ||
using System.Runtime.CompilerServices; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using System.Runtime.Loader; | ||
using Roslyn.Utilities; | ||
|
||
namespace Microsoft.CodeAnalysis; | ||
|
||
/// <summary> | ||
/// Wrapper around a real <see cref="AnalyzerFileReference"/>. An "isolated" analyzer reference is an analyzer | ||
/// reference associated with an <see cref="AssemblyLoadContext"/> that is connected to a set of other "isolated" | ||
/// analyzer references. This allows for loading the analyzers and generators from it in a way that is associated with | ||
/// that load context, keeping them separate from other analyzers and generators loaded in other load contexts, while | ||
/// also allowing all of those instances to be collected when no longer needed. Being isolated means that if any of the | ||
/// underlying assembly references change, that they can be loaded side by side with the prior references. This enables | ||
/// functionality like live reloading of analyzers and generators when they change on disk. Note: this is only | ||
/// supported on .Net Core, and not .Net Framework, as only the former has <see cref="AssemblyLoadContext"/>s. | ||
/// </summary> | ||
/// <remarks> | ||
/// The purpose of this type is to allow passing out a <see cref="AnalyzerReference"/> to the rest of the system that | ||
/// then ensures that as long as it is alive (or any <see cref="DiagnosticAnalyzer"/> or <see cref="ISourceGenerator"/> | ||
/// it passes out is alive), that the <see cref="IsolatedAnalyzerReferenceSet"/> (and its corresponding <see | ||
/// cref="AssemblyLoadContext"/>) is kept alive as well. | ||
/// </remarks> | ||
internal sealed class IsolatedAnalyzerFileReference( | ||
IsolatedAnalyzerReferenceSet isolatedAnalyzerReferenceSet, | ||
AnalyzerFileReference underlyingAnalyzerReference) | ||
: AnalyzerReference | ||
{ | ||
/// <summary> | ||
/// Conditional weak tables that ensure that as long as a particular <see cref="DiagnosticAnalyzer"/> or <see | ||
/// cref="ISourceGenerator"/> is alive, that the corresponding <see cref="IsolatedAnalyzerReferenceSet"/> (and its | ||
/// corresponding <see cref="AssemblyLoadContext"/> is kept alive. | ||
/// </summary> | ||
private static readonly ConditionalWeakTable<DiagnosticAnalyzer, IsolatedAnalyzerReferenceSet> s_analyzerToPinnedReferenceSet = []; | ||
|
||
/// <inheritdoc cref="s_analyzerToPinnedReferenceSet"/> | ||
private static readonly ConditionalWeakTable<ISourceGenerator, IsolatedAnalyzerReferenceSet> s_generatorToPinnedReferenceSet = []; | ||
|
||
/// <summary> | ||
/// We keep a strong reference here. As long as this <see cref="IsolatedAnalyzerFileReference"/> is passed out and | ||
/// held onto (say by a Project instance), it should keep the IsolatedAssemblyReferenceSet (and its ALC) alive. | ||
/// </summary> | ||
private readonly IsolatedAnalyzerReferenceSet _isolatedAnalyzerReferenceSet = isolatedAnalyzerReferenceSet; | ||
|
||
/// <summary> | ||
/// The actual real <see cref="AnalyzerReference"/> we defer our operations to. | ||
/// </summary> | ||
public readonly AnalyzerFileReference UnderlyingAnalyzerFileReference = underlyingAnalyzerReference; | ||
|
||
public override string Display => UnderlyingAnalyzerFileReference.Display; | ||
public override string? FullPath => UnderlyingAnalyzerFileReference.FullPath; | ||
public override object Id => UnderlyingAnalyzerFileReference.Id; | ||
|
||
public override ImmutableArray<DiagnosticAnalyzer> GetAnalyzers(string language) | ||
=> PinAnalyzers(static (reference, language) => reference.GetAnalyzers(language), language); | ||
|
||
public override ImmutableArray<DiagnosticAnalyzer> GetAnalyzersForAllLanguages() | ||
=> PinAnalyzers(static (reference, _) => reference.GetAnalyzersForAllLanguages(), default(VoidResult)); | ||
|
||
[Obsolete] | ||
public override ImmutableArray<ISourceGenerator> GetGenerators() | ||
=> PinGenerators(static (reference, _) => reference.GetGenerators(), default(VoidResult)); | ||
|
||
public override ImmutableArray<ISourceGenerator> GetGenerators(string language) | ||
=> PinGenerators(static (reference, language) => reference.GetGenerators(language), language); | ||
|
||
public override ImmutableArray<ISourceGenerator> GetGeneratorsForAllLanguages() | ||
=> PinGenerators(static (reference, _) => reference.GetGeneratorsForAllLanguages(), default(VoidResult)); | ||
|
||
private ImmutableArray<DiagnosticAnalyzer> PinAnalyzers<TArg>(Func<AnalyzerReference, TArg, ImmutableArray<DiagnosticAnalyzer>> getItems, TArg arg) | ||
=> PinItems(s_analyzerToPinnedReferenceSet, getItems, arg); | ||
|
||
private ImmutableArray<ISourceGenerator> PinGenerators<TArg>(Func<AnalyzerReference, TArg, ImmutableArray<ISourceGenerator>> getItems, TArg arg) | ||
=> PinItems(s_generatorToPinnedReferenceSet, getItems, arg); | ||
|
||
private ImmutableArray<TItem> PinItems<TItem, TArg>( | ||
ConditionalWeakTable<TItem, IsolatedAnalyzerReferenceSet> table, | ||
Func<AnalyzerReference, TArg, ImmutableArray<TItem>> getItems, | ||
TArg arg) | ||
where TItem : class | ||
{ | ||
// Keep a reference from each generator to the IsolatedAssemblyReferenceSet. This will ensure it (and the ALC | ||
// it points at) stays alive as long as the generator instance stays alive. | ||
var items = getItems(this.UnderlyingAnalyzerFileReference, arg); | ||
|
||
foreach (var item in items) | ||
table.TryAdd(item, _isolatedAnalyzerReferenceSet); | ||
|
||
// Note: we want to keep ourselves alive during this call so that neither we nor our reference set get GC'ed | ||
// while we're computing the items. | ||
GC.KeepAlive(this); | ||
|
||
return items; | ||
} | ||
|
||
public override bool Equals(object? obj) | ||
=> ReferenceEquals(this, obj); | ||
|
||
public override int GetHashCode() | ||
=> RuntimeHelpers.GetHashCode(this); | ||
|
||
public override string ToString() | ||
=> $"{nameof(IsolatedAnalyzerFileReference)}({UnderlyingAnalyzerFileReference})"; | ||
} | ||
|
||
#endif |
Oops, something went wrong.