-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add JSON source generator * Address review feedback * Address review feedback * Align project structure with src-gen conventions * Fix enum (de)serialization
- Loading branch information
Showing
276 changed files
with
5,107 additions
and
129 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
|
||
namespace System.Text.Json.SourceGeneration | ||
{ | ||
internal enum ClassType | ||
{ | ||
TypeUnsupportedBySourceGen = 0, | ||
Object = 1, | ||
KnownType = 2, | ||
TypeWithDesignTimeProvidedCustomConverter = 3, | ||
Enumerable = 4, | ||
Dictionary = 5, | ||
Nullable = 6, | ||
Enum = 7, | ||
} | ||
} |
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,19 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
|
||
namespace System.Text.Json.SourceGeneration | ||
{ | ||
internal enum CollectionType | ||
{ | ||
NotApplicable = 0, | ||
Array = 1, | ||
List = 2, | ||
IEnumerable = 3, | ||
IList = 4, | ||
Dictionary = 5 | ||
} | ||
} |
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,10 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
namespace System.Runtime.CompilerServices | ||
{ | ||
/// <summary> | ||
/// Dummy class so C# init-only properties can compile on NetStandard. | ||
/// </summary> | ||
internal sealed class IsExternalInit { } | ||
} |
22 changes: 22 additions & 0 deletions
22
src/libraries/System.Text.Json/gen/JsonSerializableSyntaxReceiver.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,22 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Collections.Generic; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
||
namespace System.Text.Json.SourceGeneration | ||
{ | ||
internal sealed class JsonSerializableSyntaxReceiver : ISyntaxReceiver | ||
{ | ||
public List<CompilationUnitSyntax> CompilationUnits { get; } = new(); | ||
|
||
public void OnVisitSyntaxNode(SyntaxNode syntaxNode) | ||
{ | ||
if (syntaxNode is CompilationUnitSyntax compilationUnit) | ||
{ | ||
CompilationUnits.Add(compilationUnit); | ||
} | ||
} | ||
} | ||
} |
128 changes: 128 additions & 0 deletions
128
src/libraries/System.Text.Json/gen/JsonSourceGenerator.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,128 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
using System.Text.Json.SourceGeneration.Reflection; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
||
namespace System.Text.Json.SourceGeneration | ||
{ | ||
/// <summary> | ||
/// Generates source code to optimize serialization and deserialization with JsonSerializer. | ||
/// </summary> | ||
[Generator] | ||
public sealed class JsonSourceGenerator : ISourceGenerator | ||
{ | ||
private JsonSourceGeneratorHelper? _helper; | ||
|
||
/// <summary> | ||
/// Helper for unit tests. | ||
/// </summary> | ||
public Dictionary<string, Type>? SerializableTypes => _helper.GetSerializableTypes(); | ||
|
||
/// <summary> | ||
/// Registers a syntax resolver to receive compilation units. | ||
/// </summary> | ||
/// <param name="context"></param> | ||
public void Initialize(GeneratorInitializationContext context) | ||
{ | ||
context.RegisterForSyntaxNotifications(() => new JsonSerializableSyntaxReceiver()); | ||
} | ||
|
||
/// <summary> | ||
/// Generates source code to optimize serialization and deserialization with JsonSerializer. | ||
/// </summary> | ||
/// <param name="executionContext"></param> | ||
public void Execute(GeneratorExecutionContext executionContext) | ||
{ | ||
Compilation compilation = executionContext.Compilation; | ||
|
||
const string JsonSerializableAttributeName = "System.Text.Json.Serialization.JsonSerializableAttribute"; | ||
INamedTypeSymbol jsonSerializableAttribute = compilation.GetTypeByMetadataName(JsonSerializableAttributeName); | ||
if (jsonSerializableAttribute == null) | ||
{ | ||
return; | ||
} | ||
|
||
JsonSerializableSyntaxReceiver receiver = (JsonSerializableSyntaxReceiver)executionContext.SyntaxReceiver; | ||
MetadataLoadContextInternal metadataLoadContext = new(compilation); | ||
|
||
TypeExtensions.NullableOfTType = metadataLoadContext.Resolve(typeof(Nullable<>)); | ||
|
||
JsonSourceGeneratorHelper helper = new(executionContext, metadataLoadContext); | ||
_helper = helper; | ||
|
||
// Discover serializable types indicated by JsonSerializableAttribute. | ||
foreach (CompilationUnitSyntax compilationUnit in receiver.CompilationUnits) | ||
{ | ||
SemanticModel compilationSemanticModel = executionContext.Compilation.GetSemanticModel(compilationUnit.SyntaxTree); | ||
|
||
foreach (AttributeListSyntax attributeListSyntax in compilationUnit.AttributeLists) | ||
{ | ||
AttributeSyntax attributeSyntax = attributeListSyntax.Attributes.First(); | ||
IMethodSymbol attributeSymbol = compilationSemanticModel.GetSymbolInfo(attributeSyntax).Symbol as IMethodSymbol; | ||
|
||
if (attributeSymbol == null || !jsonSerializableAttribute.Equals(attributeSymbol.ContainingType, SymbolEqualityComparer.Default)) | ||
{ | ||
// Not the right attribute. | ||
continue; | ||
} | ||
|
||
// Get JsonSerializableAttribute arguments. | ||
IEnumerable<SyntaxNode> attributeArguments = attributeSyntax.DescendantNodes().Where(node => node is AttributeArgumentSyntax); | ||
|
||
ITypeSymbol? typeSymbol = null; | ||
string? typeInfoPropertyName = null; | ||
|
||
int i = 0; | ||
foreach (AttributeArgumentSyntax node in attributeArguments) | ||
{ | ||
if (i == 0) | ||
{ | ||
TypeOfExpressionSyntax? typeNode = node.ChildNodes().Single() as TypeOfExpressionSyntax; | ||
if (typeNode != null) | ||
{ | ||
ExpressionSyntax typeNameSyntax = (ExpressionSyntax)typeNode.ChildNodes().Single(); | ||
typeSymbol = compilationSemanticModel.GetTypeInfo(typeNameSyntax).ConvertedType; | ||
} | ||
} | ||
else if (i == 1) | ||
{ | ||
// Obtain the optional TypeInfoPropertyName string property on the attribute, if present. | ||
SyntaxNode? typeInfoPropertyNameNode = node.ChildNodes().ElementAtOrDefault(1); | ||
if (typeInfoPropertyNameNode != null) | ||
{ | ||
typeInfoPropertyName = typeInfoPropertyNameNode.GetFirstToken().ValueText; | ||
} | ||
} | ||
|
||
i++; | ||
} | ||
|
||
if (typeSymbol == null) | ||
{ | ||
continue; | ||
} | ||
|
||
|
||
Type type = new TypeWrapper(typeSymbol, metadataLoadContext); | ||
if (type.Namespace == "<global namespace>") | ||
{ | ||
// typeof() reference where the type's name isn't fully qualified. | ||
// The compilation is not valid and the user needs to fix their code. | ||
// The compiler will notify the user so we don't have to. | ||
return; | ||
} | ||
|
||
helper.RegisterRootSerializableType(type, typeInfoPropertyName); | ||
} | ||
} | ||
|
||
helper.GenerateSerializationMetadata(); | ||
} | ||
} | ||
} |
Oops, something went wrong.
e98d043
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so this is the root of all evil
just kidding