diff --git a/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Emitter.cs b/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Emitter.cs
index 894ae45816ad38..2dcedb1b435ce8 100644
--- a/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Emitter.cs
+++ b/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Emitter.cs
@@ -12,97 +12,86 @@ namespace Generators
{
public partial class EventSourceGenerator
{
- private sealed class Emitter
- {
- private readonly StringBuilder _builder = new StringBuilder(1024);
- private readonly GeneratorExecutionContext _context;
-
- public Emitter(GeneratorExecutionContext context) => _context = context;
-
- public void Emit(EventSourceClass[] eventSources, CancellationToken cancellationToken)
- {
- foreach (EventSourceClass? ec in eventSources)
- {
- if (cancellationToken.IsCancellationRequested)
- {
- // stop any additional work
- break;
- }
+ /// Code for a [GeneratedCode] attribute to put on the top-level generated members.
+ private static readonly string s_generatedCodeAttribute = $"[global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"{typeof(EventSourceGenerator).Assembly.GetName().Name}\", \"{typeof(EventSourceGenerator).Assembly.GetName().Version}\")]";
- _builder.AppendLine("using System;");
- GenType(ec);
+ private static void EmitSourceFile(SourceProductionContext context, EventSourceClass ec)
+ {
+ StringBuilder sb = new StringBuilder(1024);
- _context.AddSource($"{ec.ClassName}.g.cs", SourceText.From(_builder.ToString(), Encoding.UTF8));
+ sb.AppendLine(@"// ");
+ sb.AppendLine();
+ sb.AppendLine("using System;");
+ GenType(ec, sb);
- _builder.Clear();
- }
- }
+ context.AddSource($"{ec.ClassName}.g.cs", SourceText.From(sb.ToString(), Encoding.UTF8));
+ }
- private void GenType(EventSourceClass ec)
+ private static void GenType(EventSourceClass ec, StringBuilder sb)
+ {
+ if (!string.IsNullOrWhiteSpace(ec.Namespace))
{
- if (!string.IsNullOrWhiteSpace(ec.Namespace))
- {
- _builder.AppendLine($@"
+ sb.AppendLine($@"
namespace {ec.Namespace}
{{");
- }
+ }
- _builder.AppendLine($@"
+ sb.AppendLine($@"
+ {s_generatedCodeAttribute}
partial class {ec.ClassName}
{{");
- GenerateConstructor(ec);
+ GenerateConstructor(ec, sb);
- GenerateProviderMetadata(ec.SourceName);
+ GenerateProviderMetadata(ec.SourceName, sb);
- _builder.AppendLine($@"
+ sb.AppendLine($@"
}}");
- if (!string.IsNullOrWhiteSpace(ec.Namespace))
- {
- _builder.AppendLine($@"
+ if (!string.IsNullOrWhiteSpace(ec.Namespace))
+ {
+ sb.AppendLine($@"
}}");
- }
}
+ }
- private void GenerateConstructor(EventSourceClass ec)
- {
- _builder.AppendLine($@"
+ private static void GenerateConstructor(EventSourceClass ec, StringBuilder sb)
+ {
+ sb.AppendLine($@"
private {ec.ClassName}() : base(new Guid({ec.Guid.ToString("x").Replace("{", "").Replace("}", "")}), ""{ec.SourceName}"") {{ }}");
- }
+ }
- private void GenerateProviderMetadata(string sourceName)
- {
- _builder.Append(@"
+ private static void GenerateProviderMetadata(string sourceName, StringBuilder sb)
+ {
+ sb.Append(@"
private protected override ReadOnlySpan ProviderMetadata => new byte[] { ");
- byte[] metadataBytes = MetadataForString(sourceName);
- foreach (byte b in metadataBytes)
- {
- _builder.Append($"0x{b:x}, ");
- }
-
- _builder.AppendLine(@"};");
- }
-
- // From System.Private.CoreLib
- private static byte[] MetadataForString(string name)
+ byte[] metadataBytes = MetadataForString(sourceName);
+ foreach (byte b in metadataBytes)
{
- CheckName(name);
- int metadataSize = Encoding.UTF8.GetByteCount(name) + 3;
- byte[]? metadata = new byte[metadataSize];
- ushort totalSize = checked((ushort)(metadataSize));
- metadata[0] = unchecked((byte)totalSize);
- metadata[1] = unchecked((byte)(totalSize >> 8));
- Encoding.UTF8.GetBytes(name, 0, name.Length, metadata, 2);
- return metadata;
+ sb.Append($"0x{b:x}, ");
}
- private static void CheckName(string? name)
+ sb.AppendLine(@"};");
+ }
+
+ // From System.Private.CoreLib
+ private static byte[] MetadataForString(string name)
+ {
+ CheckName(name);
+ int metadataSize = Encoding.UTF8.GetByteCount(name) + 3;
+ byte[]? metadata = new byte[metadataSize];
+ ushort totalSize = checked((ushort)(metadataSize));
+ metadata[0] = unchecked((byte)totalSize);
+ metadata[1] = unchecked((byte)(totalSize >> 8));
+ Encoding.UTF8.GetBytes(name, 0, name.Length, metadata, 2);
+ return metadata;
+ }
+
+ private static void CheckName(string? name)
+ {
+ if (name != null && 0 <= name.IndexOf('\0'))
{
- if (name != null && 0 <= name.IndexOf('\0'))
- {
- throw new ArgumentOutOfRangeException(nameof(name));
- }
+ throw new ArgumentOutOfRangeException(nameof(name));
}
}
}
diff --git a/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Parser.cs b/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Parser.cs
index 2b28b46e03335b..0ea373c9efb82b 100644
--- a/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Parser.cs
+++ b/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Parser.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
@@ -10,186 +11,139 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
-using Microsoft.CodeAnalysis.DotnetRuntime.Extensions;
namespace Generators
{
public partial class EventSourceGenerator
{
- private sealed class Parser
+ private static bool IsSyntaxTargetForGeneration(SyntaxNode node, CancellationToken cancellationToken) =>
+ node is ClassDeclarationSyntax { AttributeLists.Count: > 0 };
+
+ private static EventSourceClass? GetSemanticTargetForGeneration(GeneratorSyntaxContext context, CancellationToken cancellationToken)
{
- private readonly CancellationToken _cancellationToken;
- private readonly Compilation _compilation;
- private readonly Action _reportDiagnostic;
+ const string EventSourceAutoGenerateAttribute = "System.Diagnostics.Tracing.EventSourceAutoGenerateAttribute";
+ const string EventSourceAttribute = "System.Diagnostics.Tracing.EventSourceAttribute";
- public Parser(Compilation compilation, Action reportDiagnostic, CancellationToken cancellationToken)
- {
- _compilation = compilation;
- _cancellationToken = cancellationToken;
- _reportDiagnostic = reportDiagnostic;
- }
+ var classDef = (ClassDeclarationSyntax)context.Node;
+ SemanticModel sm = context.SemanticModel;
+ EventSourceClass? eventSourceClass = null;
- public EventSourceClass[] GetEventSourceClasses(List classDeclarations)
+ bool autoGenerate = false;
+ foreach (AttributeListSyntax cal in classDef.AttributeLists)
{
- INamedTypeSymbol? autogenerateAttribute = _compilation.GetBestTypeByMetadataName("System.Diagnostics.Tracing.EventSourceAutoGenerateAttribute");
- if (autogenerateAttribute is null)
+ foreach (AttributeSyntax ca in cal.Attributes)
{
- // No EventSourceAutoGenerateAttribute
- return Array.Empty();
- }
+ if (sm.GetSymbolInfo(ca, cancellationToken).Symbol is not IMethodSymbol caSymbol)
+ {
+ // badly formed attribute definition, or not the right attribute
+ continue;
+ }
- INamedTypeSymbol? eventSourceAttribute = _compilation.GetBestTypeByMetadataName("System.Diagnostics.Tracing.EventSourceAttribute");
- if (eventSourceAttribute is null)
- {
- // No EventSourceAttribute
- return Array.Empty();
- }
+ string attributeFullName = caSymbol.ContainingType.ToDisplayString();
- List? results = null;
- // we enumerate by syntax tree, to minimize the need to instantiate semantic models (since they're expensive)
- foreach (IGrouping? group in classDeclarations.GroupBy(x => x.SyntaxTree))
- {
- SemanticModel? sm = null;
- EventSourceClass? eventSourceClass = null;
- foreach (ClassDeclarationSyntax? classDef in group)
+ if (attributeFullName.Equals(EventSourceAutoGenerateAttribute, StringComparison.Ordinal))
+ {
+ autoGenerate = true;
+ continue;
+ }
+
+ if (!attributeFullName.Equals(EventSourceAttribute, StringComparison.Ordinal))
+ {
+ continue;
+ }
+
+ string nspace = string.Empty;
+ NamespaceDeclarationSyntax? ns = classDef.Parent as NamespaceDeclarationSyntax;
+ if (ns is null)
{
- if (_cancellationToken.IsCancellationRequested)
+ if (classDef.Parent is not CompilationUnitSyntax)
{
- // be nice and stop if we're asked to
- return results?.ToArray() ?? Array.Empty();
+ // since this generator doesn't know how to generate a nested type...
+ continue;
}
-
- bool autoGenerate = false;
- foreach (AttributeListSyntax? cal in classDef.AttributeLists)
+ }
+ else
+ {
+ nspace = ns.Name.ToString();
+ while (true)
{
- foreach (AttributeSyntax? ca in cal.Attributes)
+ ns = ns.Parent as NamespaceDeclarationSyntax;
+ if (ns == null)
{
- // need a semantic model for this tree
- sm ??= _compilation.GetSemanticModel(classDef.SyntaxTree);
-
- if (sm.GetSymbolInfo(ca, _cancellationToken).Symbol is not IMethodSymbol caSymbol)
- {
- // badly formed attribute definition, or not the right attribute
- continue;
- }
-
- if (autogenerateAttribute.Equals(caSymbol.ContainingType, SymbolEqualityComparer.Default))
- {
- autoGenerate = true;
- continue;
- }
- if (eventSourceAttribute.Equals(caSymbol.ContainingType, SymbolEqualityComparer.Default))
- {
- string nspace = string.Empty;
- NamespaceDeclarationSyntax? ns = classDef.Parent as NamespaceDeclarationSyntax;
- if (ns is null)
- {
- if (classDef.Parent is not CompilationUnitSyntax)
- {
- // since this generator doesn't know how to generate a nested type...
- continue;
- }
- }
- else
- {
- nspace = ns.Name.ToString();
- while (true)
- {
- ns = ns.Parent as NamespaceDeclarationSyntax;
- if (ns == null)
- {
- break;
- }
-
- nspace = $"{ns.Name}.{nspace}";
- }
- }
-
- string className = classDef.Identifier.ToString();
- string name = className;
- string guid = "";
-
- SeparatedSyntaxList? args = ca.ArgumentList?.Arguments;
- if (args is not null)
- {
- foreach (AttributeArgumentSyntax? arg in args)
- {
- string? argName = arg.NameEquals!.Name.Identifier.ToString();
- string? value = sm.GetConstantValue(arg.Expression, _cancellationToken).ToString();
-
- switch (argName)
- {
- case "Guid":
- guid = value;
- break;
- case "Name":
- name = value;
- break;
- }
- }
- }
-
- if (!Guid.TryParse(guid, out Guid result))
- {
- result = GenerateGuidFromName(name.ToUpperInvariant());
- }
-
- eventSourceClass = new EventSourceClass
- {
- Namespace = nspace,
- ClassName = className,
- SourceName = name,
- Guid = result
- };
- continue;
- }
+ break;
}
- }
- if (!autoGenerate)
- {
- continue;
+ nspace = $"{ns.Name}.{nspace}";
}
+ }
+
+ string className = classDef.Identifier.ToString();
+ string name = className;
+ string guid = "";
- if (eventSourceClass is null)
+ SeparatedSyntaxList? args = ca.ArgumentList?.Arguments;
+ if (args is not null)
+ {
+ foreach (AttributeArgumentSyntax arg in args)
{
- continue;
+ string argName = arg.NameEquals!.Name.Identifier.ToString();
+ string value = sm.GetConstantValue(arg.Expression, cancellationToken).ToString();
+
+ switch (argName)
+ {
+ case "Guid":
+ guid = value;
+ break;
+ case "Name":
+ name = value;
+ break;
+ }
}
+ }
- results ??= new List();
- results.Add(eventSourceClass);
+ if (!Guid.TryParse(guid, out Guid result))
+ {
+ result = GenerateGuidFromName(name.ToUpperInvariant());
}
+
+ eventSourceClass = new EventSourceClass(nspace, className, name, result);
+ continue;
}
+ }
- return results?.ToArray() ?? Array.Empty();
+ if (!autoGenerate)
+ {
+ return null;
}
- // From System.Private.CoreLib
- private static Guid GenerateGuidFromName(string name)
+ return eventSourceClass;
+ }
+
+ // From System.Private.CoreLib
+ private static Guid GenerateGuidFromName(string name)
+ {
+ ReadOnlySpan namespaceBytes = new byte[] // rely on C# compiler optimization to remove byte[] allocation
{
- ReadOnlySpan namespaceBytes = new byte[] // rely on C# compiler optimization to remove byte[] allocation
- {
0x48, 0x2C, 0x2D, 0xB2, 0xC3, 0x90, 0x47, 0xC8,
0x87, 0xF8, 0x1A, 0x15, 0xBF, 0xC1, 0x30, 0xFB,
- };
+ };
- byte[] bytes = Encoding.BigEndianUnicode.GetBytes(name);
+ byte[] bytes = Encoding.BigEndianUnicode.GetBytes(name);
- byte[] combinedBytes = new byte[namespaceBytes.Length + bytes.Length];
+ byte[] combinedBytes = new byte[namespaceBytes.Length + bytes.Length];
- bytes.CopyTo(combinedBytes, namespaceBytes.Length);
- namespaceBytes.CopyTo(combinedBytes);
+ bytes.CopyTo(combinedBytes, namespaceBytes.Length);
+ namespaceBytes.CopyTo(combinedBytes);
- using (SHA1 sha = SHA1.Create())
- {
- bytes = sha.ComputeHash(combinedBytes);
- }
+ using (SHA1 sha = SHA1.Create())
+ {
+ bytes = sha.ComputeHash(combinedBytes);
+ }
- Array.Resize(ref bytes, 16);
+ Array.Resize(ref bytes, 16);
- bytes[7] = unchecked((byte)((bytes[7] & 0x0F) | 0x50)); // Set high 4 bits of octet 7 to 5, as per RFC 4122
- return new Guid(bytes);
- }
+ bytes[7] = unchecked((byte)((bytes[7] & 0x0F) | 0x50)); // Set high 4 bits of octet 7 to 5, as per RFC 4122
+ return new Guid(bytes);
}
}
}
diff --git a/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.cs b/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.cs
index a86fef7e38ced7..cebe3a5d32c504 100644
--- a/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.cs
+++ b/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.cs
@@ -3,14 +3,15 @@
using System;
using System.Collections.Generic;
-
+using System.Collections.Immutable;
+using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Generators
{
[Generator]
- public partial class EventSourceGenerator : ISourceGenerator
+ public partial class EventSourceGenerator : IIncrementalGenerator
{
// Example input:
//
@@ -32,77 +33,16 @@ public partial class EventSourceGenerator : ISourceGenerator
// }
// }
- public void Initialize(GeneratorInitializationContext context)
- => context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
-
- public void Execute(GeneratorExecutionContext context)
- {
- SyntaxReceiver? receiver = context.SyntaxReceiver as SyntaxReceiver;
- if ((receiver?.CandidateClasses?.Count ?? 0) == 0)
- {
- // nothing to do yet
- return;
- }
-
- Parser? p = new Parser(context.Compilation, context.ReportDiagnostic, context.CancellationToken);
- EventSourceClass[]? eventSources = p.GetEventSourceClasses(receiver.CandidateClasses);
-
- if (eventSources?.Length > 0)
- {
- Emitter? e = new Emitter(context);
- e.Emit(eventSources, context.CancellationToken);
- }
- }
-
- private sealed class SyntaxReceiver : ISyntaxReceiver
+ public void Initialize(IncrementalGeneratorInitializationContext context)
{
- private List? _candidateClasses;
-
- public List? CandidateClasses => _candidateClasses;
-
- public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
- {
- // Only add classes annotated [EventSourceAutoGenerate] to reduce busy work.
- const string EventSourceAttribute = "EventSourceAutoGenerateAttribute";
- const string EventSourceAttributeShort = "EventSourceAutoGenerate";
+ IncrementalValuesProvider eventSourceClasses =
+ context.SyntaxProvider
+ .CreateSyntaxProvider(IsSyntaxTargetForGeneration, GetSemanticTargetForGeneration)
+ .Where(x => x is not null);
- // Only classes
- if (syntaxNode is ClassDeclarationSyntax classDeclaration)
- {
- // Check if has EventSource attribute before adding to candidates
- // as we don't want to add every class in the project
- foreach (AttributeListSyntax? cal in classDeclaration.AttributeLists)
- {
- foreach (AttributeSyntax? ca in cal.Attributes)
- {
- // Check if Span length matches before allocating the string to check more
- int length = ca.Name.Span.Length;
- if (length != EventSourceAttribute.Length && length != EventSourceAttributeShort.Length)
- {
- continue;
- }
-
- // Possible match, now check the string value
- string attrName = ca.Name.ToString();
- if (attrName == EventSourceAttribute || attrName == EventSourceAttributeShort)
- {
- // Match add to candidates
- _candidateClasses ??= new List();
- _candidateClasses.Add(classDeclaration);
- return;
- }
- }
- }
- }
- }
+ context.RegisterSourceOutput(eventSourceClasses, EmitSourceFile);
}
- private sealed class EventSourceClass
- {
- public string Namespace = string.Empty;
- public string ClassName = string.Empty;
- public string SourceName = string.Empty;
- public Guid Guid = Guid.Empty;
- }
+ private sealed record EventSourceClass(string Namespace, string ClassName, string SourceName, Guid Guid);
}
}
diff --git a/src/libraries/System.Private.CoreLib/gen/System.Private.CoreLib.Generators.csproj b/src/libraries/System.Private.CoreLib/gen/System.Private.CoreLib.Generators.csproj
index 6ce771f7124a87..b3274719f5a15a 100644
--- a/src/libraries/System.Private.CoreLib/gen/System.Private.CoreLib.Generators.csproj
+++ b/src/libraries/System.Private.CoreLib/gen/System.Private.CoreLib.Generators.csproj
@@ -11,8 +11,7 @@
-
-
+