Skip to content

Commit

Permalink
Make JsonGenerator be an incremental generator
Browse files Browse the repository at this point in the history
  • Loading branch information
chsienki authored and eerhardt committed Aug 24, 2021
1 parent c1c90d3 commit 92da59a
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 47 deletions.
6 changes: 3 additions & 3 deletions eng/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@
<ProjectServicingConfiguration Include="Microsoft.NETCore.App.Ref" PatchVersion="0" />
</ItemGroup>
<PropertyGroup>
<!-- For source generator support we need to target a pinned version in order to be able to run on older versions of Roslyn -->
<MicrosoftCodeAnalysisCSharpWorkspacesVersion>3.9.0</MicrosoftCodeAnalysisCSharpWorkspacesVersion>
<MicrosoftCodeAnalysisVersion>3.9.0</MicrosoftCodeAnalysisVersion>
<!-- For source generator support we are targeting the latest version of Roslyn for now, until we can support multi-targeting -->
<MicrosoftCodeAnalysisCSharpWorkspacesVersion>4.0.0-3.final</MicrosoftCodeAnalysisCSharpWorkspacesVersion>
<MicrosoftCodeAnalysisVersion>4.0.0-3.final</MicrosoftCodeAnalysisVersion>
</PropertyGroup>
<PropertyGroup>
<!-- Code analysis dependencies -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,15 @@ private sealed partial class Emitter
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);

private readonly GeneratorExecutionContext _executionContext;
private readonly SourceProductionContext _sourceProductionContext;

private ContextGenerationSpec _currentContext = null!;

private readonly SourceGenerationSpec _generationSpec = null!;

public Emitter(in GeneratorExecutionContext executionContext, SourceGenerationSpec generationSpec)
public Emitter(in SourceProductionContext sourceProductionContext, SourceGenerationSpec generationSpec)
{
_executionContext = executionContext;
_sourceProductionContext = sourceProductionContext;
_generationSpec = generationSpec;
}

Expand Down Expand Up @@ -165,7 +165,7 @@ namespace {@namespace}
sb.AppendLine("}");
}

_executionContext.AddSource(fileName, SourceText.From(sb.ToString(), Encoding.UTF8));
_sourceProductionContext.AddSource(fileName, SourceText.From(sb.ToString(), Encoding.UTF8));
}

private void GenerateTypeInfo(TypeGenerationSpec typeGenerationSpec)
Expand Down Expand Up @@ -242,7 +242,7 @@ private void GenerateTypeInfo(TypeGenerationSpec typeGenerationSpec)
break;
case ClassType.TypeUnsupportedBySourceGen:
{
_executionContext.ReportDiagnostic(
_sourceProductionContext.ReportDiagnostic(
Diagnostic.Create(TypeNotSupported, Location.None, new string[] { typeGenerationSpec.TypeRef }));
return;
}
Expand All @@ -258,7 +258,7 @@ private void GenerateTypeInfo(TypeGenerationSpec typeGenerationSpec)
}
catch (ArgumentException)
{
_executionContext.ReportDiagnostic(Diagnostic.Create(DuplicateTypeName, Location.None, new string[] { typeGenerationSpec.TypeInfoPropertyName }));
_sourceProductionContext.ReportDiagnostic(Diagnostic.Create(DuplicateTypeName, Location.None, new string[] { typeGenerationSpec.TypeInfoPropertyName }));
}
}

Expand Down
19 changes: 11 additions & 8 deletions src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
Expand Down Expand Up @@ -31,7 +32,8 @@ private sealed class Parser
private const string JsonPropertyNameAttributeFullName = "System.Text.Json.Serialization.JsonPropertyNameAttribute";
private const string JsonPropertyOrderAttributeFullName = "System.Text.Json.Serialization.JsonPropertyOrderAttribute";

private readonly GeneratorExecutionContext _executionContext;
private readonly Compilation _compilation;
private readonly SourceProductionContext _sourceProductionContext;
private readonly MetadataLoadContextInternal _metadataLoadContext;

private readonly Type _ilistOfTType;
Expand Down Expand Up @@ -96,10 +98,11 @@ private sealed class Parser
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);

public Parser(in GeneratorExecutionContext executionContext)
public Parser(Compilation compilation, in SourceProductionContext sourceProductionContext)
{
_executionContext = executionContext;
_metadataLoadContext = new MetadataLoadContextInternal(executionContext.Compilation);
_compilation = compilation;
_sourceProductionContext = sourceProductionContext;
_metadataLoadContext = new MetadataLoadContextInternal(_compilation);

_ilistOfTType = _metadataLoadContext.Resolve(SpecialType.System_Collections_Generic_IList_T);
_icollectionOfTType = _metadataLoadContext.Resolve(SpecialType.System_Collections_Generic_ICollection_T);
Expand Down Expand Up @@ -138,9 +141,9 @@ public Parser(in GeneratorExecutionContext executionContext)
PopulateKnownTypes();
}

public SourceGenerationSpec? GetGenerationSpec(List<ClassDeclarationSyntax> classDeclarationSyntaxList)
public SourceGenerationSpec? GetGenerationSpec(ImmutableArray<ClassDeclarationSyntax> classDeclarationSyntaxList)
{
Compilation compilation = _executionContext.Compilation;
Compilation compilation = _compilation;
INamedTypeSymbol jsonSerializerContextSymbol = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonSerializerContext");
INamedTypeSymbol jsonSerializableAttributeSymbol = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonSerializableAttribute");
INamedTypeSymbol jsonSourceGenerationOptionsAttributeSymbol = compilation.GetTypeByMetadataName("System.Text.Json.Serialization.JsonSourceGenerationOptionsAttribute");
Expand Down Expand Up @@ -198,7 +201,7 @@ public Parser(in GeneratorExecutionContext executionContext)
if (!TryGetClassDeclarationList(contextTypeSymbol, out List<string> classDeclarationList))
{
// Class or one of its containing types is not partial so we can't add to it.
_executionContext.ReportDiagnostic(Diagnostic.Create(ContextClassesMustBePartial, Location.None, new string[] { contextTypeSymbol.Name }));
_sourceProductionContext.ReportDiagnostic(Diagnostic.Create(ContextClassesMustBePartial, Location.None, new string[] { contextTypeSymbol.Name }));
continue;
}

Expand Down Expand Up @@ -729,7 +732,7 @@ private TypeGenerationSpec GetOrAddTypeGenerationSpec(Type type, JsonSourceGener
if (!type.TryGetDeserializationConstructor(useDefaultCtorInAnnotatedStructs, out ConstructorInfo? constructor))
{
classType = ClassType.TypeUnsupportedBySourceGen;
_executionContext.ReportDiagnostic(Diagnostic.Create(MultipleJsonConstructorAttribute, Location.None, new string[] { $"{type}" }));
_sourceProductionContext.ReportDiagnostic(Diagnostic.Create(MultipleJsonConstructorAttribute, Location.None, new string[] { $"{type}" }));
}
else
{
Expand Down
48 changes: 18 additions & 30 deletions src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
Expand All @@ -16,59 +17,46 @@ namespace System.Text.Json.SourceGeneration
/// Generates source code to optimize serialization and deserialization with JsonSerializer.
/// </summary>
[Generator]
public sealed partial class JsonSourceGenerator : ISourceGenerator
public sealed partial class JsonSourceGenerator : IIncrementalGenerator
{
/// <summary>
/// Registers a syntax resolver to receive compilation units.
/// </summary>
/// <param name="context"></param>
public void Initialize(GeneratorInitializationContext context)
private const string SystemTextJsonSourceGenerationName = "System.Text.Json.SourceGeneration";
private const string IJsonOnSerializedFullName = "System.Text.Json.Serialization.IJsonOnSerialized";
private const string IJsonOnSerializingFullName = "System.Text.Json.Serialization.IJsonOnSerializing";


public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
var classDeclarations = context.SyntaxProvider.CreateSyntaxProvider((s, _) => s is ClassDeclarationSyntax, (s, _) => (ClassDeclarationSyntax)s.Node);

var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect());

context.RegisterSourceOutput(compilationAndClasses, (spc, source) => Execute(source.Left, source.Right, spc));
}

/// <summary>
/// Generates source code to optimize serialization and deserialization with JsonSerializer.
/// </summary>
/// <param name="executionContext"></param>
public void Execute(GeneratorExecutionContext executionContext)
private void Execute(Compilation compilation, ImmutableArray<ClassDeclarationSyntax> contextClasses, SourceProductionContext context)
{
#if LAUNCH_DEBUGGER
if (!Diagnostics.Debugger.IsAttached)
{
Diagnostics.Debugger.Launch();
}
#endif
SyntaxReceiver receiver = (SyntaxReceiver)executionContext.SyntaxReceiver;
List<ClassDeclarationSyntax>? contextClasses = receiver.ClassDeclarationSyntaxList;
if (contextClasses == null)
if (contextClasses.IsDefaultOrEmpty)
{
return;
}

Parser parser = new(executionContext);
SourceGenerationSpec? spec = parser.GetGenerationSpec(receiver.ClassDeclarationSyntaxList);
Parser parser = new(compilation, context);
SourceGenerationSpec? spec = parser.GetGenerationSpec(contextClasses);
if (spec != null)
{
_rootTypes = spec.ContextGenerationSpecList[0].RootSerializableTypes;

Emitter emitter = new(executionContext, spec);
Emitter emitter = new(context, spec);
emitter.Emit();
}
}

private sealed class SyntaxReceiver : ISyntaxReceiver
{
public List<ClassDeclarationSyntax>? ClassDeclarationSyntaxList { get; private set; }

public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is ClassDeclarationSyntax { AttributeLists.Count: > 0, BaseList.Types.Count: > 0 } cds)
{
(ClassDeclarationSyntaxList ??= new List<ClassDeclarationSyntax>()).Add(cds);
}
}
}

/// <summary>
/// Helper for unit tests.
Expand Down

0 comments on commit 92da59a

Please sign in to comment.