Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a general-purpose property bag to Compilation. #5708

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Compilers/Core/Portable/CodeAnalysis.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
<Compile Include="Binding\AbstractLookupSymbolsInfo.cs" />
<Compile Include="CaseInsensitiveComparison.cs" />
<Compile Include="CodeGen\ILEmitStyle.cs" />
<Compile Include="Compilation\CompilationContext.cs" />
<Compile Include="DiagnosticAnalyzer\AnalyzerDriver.CompilationData.cs" />
<Compile Include="DiagnosticAnalyzer\SuppressMessageInfo.cs" />
<Compile Include="Diagnostic\SuppressionInfo.cs" />
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/Core/Portable/Compilation/Compilation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ namespace Microsoft.CodeAnalysis
/// </summary>
public abstract partial class Compilation
{
/// <summary>
/// For use only in <see cref="Microsoft.CodeAnalysis.Collections.CompilationContext"/>.
/// </summary>
internal ConcurrentDictionary<object, object> _compilationContext = new ConcurrentDictionary<object, object>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My original comment is gone now, but I'll re-iterate: Make this a lazily allocated dictionary. An empty ConcurrentDictionary carries quite a bit of overhead.

The canonical pattern is:

private ConcurrentDictionary<object, object> _compilationContext;
internal ConcurrentDictionary<object, object> CompilationContext => LazyInitializer.EnsureInitialized(ref _compilationContext);

Another way to mitigate the allocation hit for an empty ConcurrentDictionary is to specify a smaller concurrency level (the default is 4 times the processor count) and initial capacity (default is 31).


/// <summary>
/// Returns true if this is a case sensitive compilation, false otherwise. Case sensitivity
/// affects compilation features such as name lookup as well as choosing what names to emit
Expand Down
66 changes: 66 additions & 0 deletions src/Compilers/Core/Portable/Compilation/CompilationContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copyright?


namespace Microsoft.CodeAnalysis.Collections
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should CompilationContext be in the Collection namespace? Its name doesn't quite sound as a collection.

{
/// <summary>
/// A mutable typesafe dictionary from a pair of compilation and key (of type <see cref="CompilationContext.Key{T}"/>)
/// to a value (of type T). All keys and values associated with a compilation are reachable for GC purposes as a
/// consequence of this API for only as long as the compilation is reachable.
/// </summary>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding information about thread-safety.

public class CompilationContext
{
private static CompilationContext _instance = new CompilationContext();
public static CompilationContext Instance => _instance;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to have an instance of this type at all? Should the type be static?

private CompilationContext() { }

public sealed class Key<T> { }

public bool TryGetValue<T>(Compilation compilation, Key<T> key, out T value)
{
if (compilation == null) throw new ArgumentNullException(nameof(compilation));
if (key == null) throw new ArgumentNullException(nameof(key));
object tempValue;
if (compilation._compilationContext.TryGetValue(key, out tempValue))
{
value = (T)tempValue;
return true;
}
else
{
value = default(T);
return false;
}
}

public bool TryAdd<T>(Compilation compilation, Key<T> key, T value)
{
if (compilation == null) throw new ArgumentNullException(nameof(compilation));
if (key == null) throw new ArgumentNullException(nameof(key));
return compilation._compilationContext.TryAdd(key, value);
}

public bool TryRemove<T>(Compilation compilation, Key<T> key, out T value)
{
if (compilation == null) throw new ArgumentNullException(nameof(compilation));
if (key == null) throw new ArgumentNullException(nameof(key));
object tempValue;
if (compilation._compilationContext.TryRemove(key, out tempValue))
{
value = (T)tempValue;
return true;
}
else
{
value = default(T);
return false;
}
}

public T GetOrAdd<T>(Compilation compilation, Key<T> key, T value)
{
if (compilation == null) throw new ArgumentNullException(nameof(compilation));
if (key == null) throw new ArgumentNullException(nameof(key));
return (T)compilation._compilationContext.GetOrAdd(key, value);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like there should be a way to change a value for a particular key.

}
11 changes: 9 additions & 2 deletions src/Compilers/Core/Portable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
*REMOVED*static Microsoft.CodeAnalysis.SyntaxNodeExtensions.NormalizeWhitespace<TNode>(this TNode node, string indentation = " ", bool elasticTrivia = false) -> TNode
Microsoft.CodeAnalysis.Collections.CompilationContext
Microsoft.CodeAnalysis.Collections.CompilationContext.GetOrAdd<T>(Microsoft.CodeAnalysis.Compilation compilation, Microsoft.CodeAnalysis.Collections.CompilationContext.Key<T> key, T value) -> T
Microsoft.CodeAnalysis.Collections.CompilationContext.Key<T>
Microsoft.CodeAnalysis.Collections.CompilationContext.TryAdd<T>(Microsoft.CodeAnalysis.Compilation compilation, Microsoft.CodeAnalysis.Collections.CompilationContext.Key<T> key, T value) -> bool
Microsoft.CodeAnalysis.Collections.CompilationContext.TryGetValue<T>(Microsoft.CodeAnalysis.Compilation compilation, Microsoft.CodeAnalysis.Collections.CompilationContext.Key<T> key, out T value) -> bool
Microsoft.CodeAnalysis.Collections.CompilationContext.TryRemove<T>(Microsoft.CodeAnalysis.Compilation compilation, Microsoft.CodeAnalysis.Collections.CompilationContext.Key<T> key, out T value) -> bool
Microsoft.CodeAnalysis.CommandLineArguments.ScriptArguments.get -> System.Collections.Immutable.ImmutableArray<string>
Microsoft.CodeAnalysis.CommandLineParser.IsInteractive.get -> bool
Microsoft.CodeAnalysis.Compilation.GetSubmissionResultType(out bool hasValue) -> Microsoft.CodeAnalysis.ITypeSymbol
Microsoft.CodeAnalysis.Compilation.PreviousSubmission.get -> Microsoft.CodeAnalysis.Compilation
Microsoft.CodeAnalysis.Compilation.ScriptClass.get -> Microsoft.CodeAnalysis.INamedTypeSymbol
Microsoft.CodeAnalysis.Compilation.WithPreviousSubmission(Microsoft.CodeAnalysis.Compilation newPreviousSubmission) -> Microsoft.CodeAnalysis.Compilation
Microsoft.CodeAnalysis.CompilationOptions.ReportSuppressedDiagnostics.get -> bool
Microsoft.CodeAnalysis.CompilationOptions.Deterministic.get -> bool
Microsoft.CodeAnalysis.CompilationOptions.ReportSuppressedDiagnostics.get -> bool
Microsoft.CodeAnalysis.CompilationOptions.WithReportSuppressedDiagnostics(bool value) -> Microsoft.CodeAnalysis.CompilationOptions
Microsoft.CodeAnalysis.Diagnostic.GetSuppressionInfo(Microsoft.CodeAnalysis.Compilation compilation) -> Microsoft.CodeAnalysis.Diagnostics.SuppressionInfo
Microsoft.CodeAnalysis.DiagnosticDescriptor.GetEffectiveSeverity(Microsoft.CodeAnalysis.CompilationOptions compilationOptions) -> Microsoft.CodeAnalysis.ReportDiagnostic
Expand Down Expand Up @@ -54,6 +60,7 @@ Microsoft.CodeAnalysis.StrongNameProvider.StrongNameProvider() -> void
abstract Microsoft.CodeAnalysis.Diagnostic.IsSuppressed.get -> bool
abstract Microsoft.CodeAnalysis.ParseOptions.CommonWithKind(Microsoft.CodeAnalysis.SourceCodeKind kind) -> Microsoft.CodeAnalysis.ParseOptions
override Microsoft.CodeAnalysis.SyntaxTree.ToString() -> string
static Microsoft.CodeAnalysis.Collections.CompilationContext.Instance.get -> Microsoft.CodeAnalysis.Collections.CompilationContext
static Microsoft.CodeAnalysis.Diagnostic.Create(string id, string category, Microsoft.CodeAnalysis.LocalizableString message, Microsoft.CodeAnalysis.DiagnosticSeverity severity, Microsoft.CodeAnalysis.DiagnosticSeverity defaultSeverity, bool isEnabledByDefault, int warningLevel, bool isSuppressed, Microsoft.CodeAnalysis.LocalizableString title = null, Microsoft.CodeAnalysis.LocalizableString description = null, string helpLink = null, Microsoft.CodeAnalysis.Location location = null, System.Collections.Generic.IEnumerable<Microsoft.CodeAnalysis.Location> additionalLocations = null, System.Collections.Generic.IEnumerable<string> customTags = null, System.Collections.Immutable.ImmutableDictionary<string, string> properties = null) -> Microsoft.CodeAnalysis.Diagnostic
static Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzerExtensions.WithAnalyzers(this Microsoft.CodeAnalysis.Compilation compilation, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer> analyzers, Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzersOptions analysisOptions) -> Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzers
static Microsoft.CodeAnalysis.Diagnostics.Telemetry.AnalyzerTelemetry.GetAnalyzerActionCountsAsync(this Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzers compilationWithAnalyzers, Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer analyzer, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Diagnostics.Telemetry.AnalyzerTelemetry.ActionCounts>
Expand All @@ -62,4 +69,4 @@ static Microsoft.CodeAnalysis.SyntaxNodeExtensions.NormalizeWhitespace<TNode>(th
static Microsoft.CodeAnalysis.SyntaxNodeExtensions.NormalizeWhitespace<TNode>(this TNode node, string indentation, bool elasticTrivia) -> TNode
virtual Microsoft.CodeAnalysis.MetadataReferenceResolver.ResolveMissingAssemblies.get -> bool
virtual Microsoft.CodeAnalysis.MetadataReferenceResolver.ResolveMissingAssembly(Microsoft.CodeAnalysis.AssemblyIdentity identity) -> Microsoft.CodeAnalysis.PortableExecutableReference
virtual Microsoft.CodeAnalysis.SourceReferenceResolver.ReadText(string resolvedPath) -> Microsoft.CodeAnalysis.Text.SourceText
virtual Microsoft.CodeAnalysis.SourceReferenceResolver.ReadText(string resolvedPath) -> Microsoft.CodeAnalysis.Text.SourceText