diff --git a/docs/project/list-of-diagnostics.md b/docs/project/list-of-diagnostics.md
index 47ec247968859..f12b4ef549192 100644
--- a/docs/project/list-of-diagnostics.md
+++ b/docs/project/list-of-diagnostics.md
@@ -108,3 +108,13 @@ The diagnostic id values reserved for .NET Libraries analyzer warnings are `SYSL
| __`SYSLIB1027`__ | *_`SYSLIB1024`-`SYSLIB1029` reserved for logging._* |
| __`SYSLIB1028`__ | *_`SYSLIB1024`-`SYSLIB1029` reserved for logging._* |
| __`SYSLIB1029`__ | *_`SYSLIB1024`-`SYSLIB1029` reserved for logging._* |
+| __`SYSLIB1030`__ | [System.Text.Json.SourceGeneration] Did not generate serialization metadata for type. |
+| __`SYSLIB1031`__ | [System.Text.Json.SourceGeneration] Duplicate type name. |
+| __`SYSLIB1032`__ | *_`SYSLIB1032`-`SYSLIB1039` reserved for System.Text.Json.SourceGeneration._* |
+| __`SYSLIB1033`__ | *_`SYSLIB1032`-`SYSLIB1039` reserved for System.Text.Json.SourceGeneration._* |
+| __`SYSLIB1034`__ | *_`SYSLIB1032`-`SYSLIB1039` reserved for System.Text.Json.SourceGeneration._* |
+| __`SYSLIB1035`__ | *_`SYSLIB1032`-`SYSLIB1039` reserved for System.Text.Json.SourceGeneration._* |
+| __`SYSLIB1036`__ | *_`SYSLIB1032`-`SYSLIB1039` reserved for System.Text.Json.SourceGeneration._* |
+| __`SYSLIB1037`__ | *_`SYSLIB1032`-`SYSLIB1039` reserved for System.Text.Json.SourceGeneration._* |
+| __`SYSLIB1038`__ | *_`SYSLIB1032`-`SYSLIB1039` reserved for System.Text.Json.SourceGeneration._* |
+| __`SYSLIB1039`__ | *_`SYSLIB1032`-`SYSLIB1039` reserved for System.Text.Json.SourceGeneration._* |
diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/IsExternalInit.cs b/src/libraries/Common/src/System/Runtime/CompilerServices/IsExternalInit.cs
similarity index 82%
rename from src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/IsExternalInit.cs
rename to src/libraries/Common/src/System/Runtime/CompilerServices/IsExternalInit.cs
index f9973999dde93..0f1bb5d6672b0 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/IsExternalInit.cs
+++ b/src/libraries/Common/src/System/Runtime/CompilerServices/IsExternalInit.cs
@@ -10,7 +10,12 @@ namespace System.Runtime.CompilerServices
/// This class should not be used by developers in source code.
///
[EditorBrowsable(EditorBrowsableState.Never)]
- public static class IsExternalInit
+#if SYSTEM_PRIVATE_CORELIB
+ public
+#else
+ internal
+#endif
+ static class IsExternalInit
{
}
}
diff --git a/src/libraries/Microsoft.CSharp/tests/IsExternalInit.cs b/src/libraries/Microsoft.CSharp/tests/IsExternalInit.cs
deleted file mode 100644
index 39d98e797e533..0000000000000
--- a/src/libraries/Microsoft.CSharp/tests/IsExternalInit.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-// 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
-{
- public sealed class IsExternalInit
- {
- }
-}
diff --git a/src/libraries/Microsoft.CSharp/tests/Microsoft.CSharp.Tests.csproj b/src/libraries/Microsoft.CSharp/tests/Microsoft.CSharp.Tests.csproj
index c115e0c43497f..fd58dca4e44f6 100644
--- a/src/libraries/Microsoft.CSharp/tests/Microsoft.CSharp.Tests.csproj
+++ b/src/libraries/Microsoft.CSharp/tests/Microsoft.CSharp.Tests.csproj
@@ -11,6 +11,7 @@
true
+
@@ -28,7 +29,6 @@
-
diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
index b8f3489e46526..55e6938440c4a 100644
--- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
+++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
@@ -684,7 +684,6 @@
-
@@ -1165,6 +1164,9 @@
Common\System\Diagnostics\CodeAnalysis\ExcludeFromCodeCoverageAttribute.cs
+
+ Common\System\Runtime\CompilerServices\IsExternalInit.cs
+
Common\System\Runtime\Versioning\NonVersionableAttribute.cs
diff --git a/src/libraries/System.Text.Json/gen/IsExternalInit.cs b/src/libraries/System.Text.Json/gen/IsExternalInit.cs
deleted file mode 100644
index d5984b4be3835..0000000000000
--- a/src/libraries/System.Text.Json/gen/IsExternalInit.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-// 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
-{
- ///
- /// Dummy class so C# init-only properties can compile on NetStandard.
- ///
- internal sealed class IsExternalInit { }
-}
diff --git a/src/libraries/System.Text.Json/gen/JsonSerializableSyntaxReceiver.cs b/src/libraries/System.Text.Json/gen/JsonSerializableSyntaxReceiver.cs
deleted file mode 100644
index f60f4cfb686f9..0000000000000
--- a/src/libraries/System.Text.Json/gen/JsonSerializableSyntaxReceiver.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-// 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 CompilationUnits { get; } = new();
-
- public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
- {
- if (syntaxNode is CompilationUnitSyntax compilationUnit)
- {
- CompilationUnits.Add(compilationUnit);
- }
- }
- }
-}
diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs
new file mode 100644
index 0000000000000..a176de61b9a2f
--- /dev/null
+++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs
@@ -0,0 +1,682 @@
+// 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.Diagnostics;
+using System.Linq;
+using System.Text.Json.Serialization;
+using System.Text.Json.SourceGeneration.Reflection;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+
+namespace System.Text.Json.SourceGeneration
+{
+ public sealed partial class JsonSourceGenerator
+ {
+ private sealed class Emitter
+ {
+ private const string RuntimeCustomConverterFetchingMethodName = "GetRuntimeProvidedCustomConverter";
+
+ private const string JsonContextDeclarationSource = "internal partial class JsonContext : JsonSerializerContext";
+
+ private const string OptionsInstanceVariableName = "Options";
+
+ private const string PropInitFuncVarName = "PropInitFunc";
+
+ private const string JsonMetadataServicesClassName = "JsonMetadataServices";
+
+ private const string CreateValueInfoMethodName = "CreateValueInfo";
+
+ private const string SystemTextJsonSourceGenerationName = "System.Text.Json.SourceGeneration";
+
+ private static DiagnosticDescriptor TypeNotSupported { get; } = new DiagnosticDescriptor(
+ id: "SYSLIB1030",
+ title: new LocalizableResourceString(nameof(SR.TypeNotSupportedTitle), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
+ messageFormat: new LocalizableResourceString(nameof(SR.TypeNotSupportedMessageFormat), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
+ category: SystemTextJsonSourceGenerationName,
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
+
+ private static DiagnosticDescriptor DuplicateTypeName { get; } = new DiagnosticDescriptor(
+ id: "SYSLIB1031",
+ title: new LocalizableResourceString(nameof(SR.DuplicateTypeNameTitle), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
+ messageFormat: new LocalizableResourceString(nameof(SR.DuplicateTypeNameMessageFormat), SR.ResourceManager, typeof(FxResources.System.Text.Json.SourceGeneration.SR)),
+ category: SystemTextJsonSourceGenerationName,
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
+
+ private readonly string _generationNamespace;
+
+ // TODO (https://github.com/dotnet/runtime/issues/52218): consider public option for this.
+ // Converter-honoring logic generation can be simplified
+ // if we don't plan to have a feature around this.
+ private readonly bool _honorRuntimeProvidedCustomConverters = true;
+
+ private readonly GeneratorExecutionContext _executionContext;
+
+ ///
+ /// Types that we have initiated serialization metadata generation for. A type may be discoverable in the object graph,
+ /// but not reachable for serialization (e.g. it is [JsonIgnore]'d); thus we maintain a separate cache.
+ ///
+ private readonly HashSet _typesWithMetadataGenerated = new();
+
+ ///
+ /// Types that were specified with System.Text.Json.Serialization.JsonSerializableAttribute.
+ ///
+ private readonly Dictionary _rootSerializableTypes = null!;
+
+ public Emitter(in GeneratorExecutionContext executionContext, Dictionary rootSerializableTypes)
+ {
+ _executionContext = executionContext;
+ _generationNamespace = $"{executionContext.Compilation.AssemblyName}.JsonSourceGeneration";
+ _rootSerializableTypes = rootSerializableTypes;
+ }
+
+ public void Emit()
+ {
+ foreach (KeyValuePair pair in _rootSerializableTypes)
+ {
+ TypeMetadata typeMetadata = pair.Value;
+ GenerateTypeMetadata(typeMetadata);
+ }
+
+ // Add base default instance source.
+ _executionContext.AddSource("JsonContext.g.cs", SourceText.From(GetBaseJsonContextImplementation(), Encoding.UTF8));
+
+ // Add GetJsonTypeInfo override implementation.
+ _executionContext.AddSource("JsonContext.GetJsonTypeInfo.g.cs", SourceText.From(GetGetTypeInfoImplementation(), Encoding.UTF8));
+ }
+
+ private void GenerateTypeMetadata(TypeMetadata typeMetadata)
+ {
+ Debug.Assert(typeMetadata != null);
+
+ if (_typesWithMetadataGenerated.Contains(typeMetadata))
+ {
+ return;
+ }
+
+ _typesWithMetadataGenerated.Add(typeMetadata);
+
+ string source;
+
+ switch (typeMetadata.ClassType)
+ {
+ case ClassType.KnownType:
+ {
+ source = GenerateForTypeWithKnownConverter(typeMetadata);
+ }
+ break;
+ case ClassType.TypeWithDesignTimeProvidedCustomConverter:
+ {
+ source = GenerateForTypeWithUnknownConverter(typeMetadata);
+ }
+ break;
+ case ClassType.Nullable:
+ {
+ source = GenerateForNullable(typeMetadata);
+
+ GenerateTypeMetadata(typeMetadata.NullableUnderlyingTypeMetadata);
+ }
+ break;
+ case ClassType.Enum:
+ {
+ source = GenerateForEnum(typeMetadata);
+ }
+ break;
+ case ClassType.Enumerable:
+ {
+ source = GenerateForCollection(typeMetadata);
+
+ GenerateTypeMetadata(typeMetadata.CollectionValueTypeMetadata);
+ }
+ break;
+ case ClassType.Dictionary:
+ {
+ source = GenerateForCollection(typeMetadata);
+
+ GenerateTypeMetadata(typeMetadata.CollectionKeyTypeMetadata);
+ GenerateTypeMetadata(typeMetadata.CollectionValueTypeMetadata);
+ }
+ break;
+ case ClassType.Object:
+ {
+ source = GenerateForObject(typeMetadata);
+
+ if (typeMetadata.PropertiesMetadata != null)
+ {
+ foreach (PropertyMetadata metadata in typeMetadata.PropertiesMetadata)
+ {
+ GenerateTypeMetadata(metadata.TypeMetadata);
+ }
+ }
+ }
+ break;
+ case ClassType.TypeUnsupportedBySourceGen:
+ {
+ _executionContext.ReportDiagnostic(
+ Diagnostic.Create(TypeNotSupported, Location.None, new string[] { typeMetadata.CompilableName }));
+ return;
+ }
+ default:
+ {
+ throw new InvalidOperationException();
+ }
+ }
+
+ try
+ {
+ _executionContext.AddSource($"{typeMetadata.FriendlyName}.cs", SourceText.From(source, Encoding.UTF8));
+ }
+ catch (ArgumentException)
+ {
+ _executionContext.ReportDiagnostic(Diagnostic.Create(DuplicateTypeName, Location.None, new string[] { typeMetadata.FriendlyName }));
+ }
+ }
+
+ private string GenerateForTypeWithKnownConverter(TypeMetadata typeMetadata)
+ {
+ string typeCompilableName = typeMetadata.CompilableName;
+ string typeFriendlyName = typeMetadata.FriendlyName;
+
+ string metadataInitSource = $@"_{typeFriendlyName} = {JsonMetadataServicesClassName}.{GetCreateValueInfoMethodRef(typeCompilableName)}({OptionsInstanceVariableName}, {JsonMetadataServicesClassName}.{typeFriendlyName}Converter);";
+
+ return GenerateForType(typeMetadata, metadataInitSource);
+ }
+
+ private string GenerateForTypeWithUnknownConverter(TypeMetadata typeMetadata)
+ {
+ string typeCompilableName = typeMetadata.CompilableName;
+ string typeFriendlyName = typeMetadata.FriendlyName;
+
+ StringBuilder sb = new();
+
+ // TODO (https://github.com/dotnet/runtime/issues/52218): consider moving this verification source to common helper.
+ string metadataInitSource = $@"JsonConverter converter = {typeMetadata.ConverterInstantiationLogic};
+ Type typeToConvert = typeof({typeCompilableName});
+ if (!converter.CanConvert(typeToConvert))
+ {{
+ Type underlyingType = Nullable.GetUnderlyingType(typeToConvert);
+ if (underlyingType != null && converter.CanConvert(underlyingType))
+ {{
+ JsonConverter actualConverter = converter;
+
+ if (converter is JsonConverterFactory converterFactory)
+ {{
+ actualConverter = converterFactory.CreateConverter(underlyingType, {OptionsInstanceVariableName});
+
+ if (actualConverter == null || actualConverter is JsonConverterFactory)
+ {{
+ throw new InvalidOperationException($""JsonConverterFactory '{{converter}} cannot return a 'null' or 'JsonConverterFactory' value."");
+ }}
+ }}
+
+ // Allow nullable handling to forward to the underlying type's converter.
+ converter = {JsonMetadataServicesClassName}.GetNullableConverter<{typeCompilableName}>((JsonConverter<{typeCompilableName}>)actualConverter);
+ }}
+ else
+ {{
+ throw new InvalidOperationException($""The converter '{{converter.GetType()}}' is not compatible with the type '{{typeToConvert}}'."");
+ }}
+ }}
+
+ _{typeFriendlyName} = {JsonMetadataServicesClassName}.{GetCreateValueInfoMethodRef(typeCompilableName)}({OptionsInstanceVariableName}, converter);";
+
+ return GenerateForType(typeMetadata, metadataInitSource);
+ }
+
+ private string GenerateForNullable(TypeMetadata typeMetadata)
+ {
+ string typeCompilableName = typeMetadata.CompilableName;
+ string typeFriendlyName = typeMetadata.FriendlyName;
+
+ TypeMetadata? underlyingTypeMetadata = typeMetadata.NullableUnderlyingTypeMetadata;
+ Debug.Assert(underlyingTypeMetadata != null);
+ string underlyingTypeCompilableName = underlyingTypeMetadata.CompilableName;
+ string underlyingTypeFriendlyName = underlyingTypeMetadata.FriendlyName;
+ string underlyingTypeInfoNamedArg = underlyingTypeMetadata.ClassType == ClassType.TypeUnsupportedBySourceGen
+ ? "underlyingTypeInfo: null"
+ : $"underlyingTypeInfo: {underlyingTypeFriendlyName}";
+
+ string metadataInitSource = @$"_{typeFriendlyName} = {JsonMetadataServicesClassName}.{GetCreateValueInfoMethodRef(typeCompilableName)}(
+ {OptionsInstanceVariableName},
+ {JsonMetadataServicesClassName}.GetNullableConverter<{underlyingTypeCompilableName}>({underlyingTypeInfoNamedArg}));
+";
+
+ return GenerateForType(typeMetadata, metadataInitSource);
+ }
+
+ private string GenerateForEnum(TypeMetadata typeMetadata)
+ {
+ string typeCompilableName = typeMetadata.CompilableName;
+ string typeFriendlyName = typeMetadata.FriendlyName;
+
+ string metadataInitSource = $"_{typeFriendlyName} = {JsonMetadataServicesClassName}.{GetCreateValueInfoMethodRef(typeCompilableName)}({OptionsInstanceVariableName}, JsonMetadataServices.GetEnumConverter<{typeCompilableName}>({OptionsInstanceVariableName}));";
+
+ return GenerateForType(typeMetadata, metadataInitSource);
+ }
+
+ private string GenerateForCollection(TypeMetadata typeMetadata)
+ {
+ string typeCompilableName = typeMetadata.CompilableName;
+ string typeFriendlyName = typeMetadata.FriendlyName;
+
+ // Key metadata
+ TypeMetadata? collectionKeyTypeMetadata = typeMetadata.CollectionKeyTypeMetadata;
+ Debug.Assert(!(typeMetadata.CollectionType == CollectionType.Dictionary && collectionKeyTypeMetadata == null));
+ string? keyTypeCompilableName = collectionKeyTypeMetadata?.CompilableName;
+ string? keyTypeReadableName = collectionKeyTypeMetadata?.FriendlyName;
+
+ string? keyTypeMetadataPropertyName;
+ if (typeMetadata.ClassType != ClassType.Dictionary)
+ {
+ keyTypeMetadataPropertyName = "null";
+ }
+ else
+ {
+ keyTypeMetadataPropertyName = collectionKeyTypeMetadata.ClassType == ClassType.TypeUnsupportedBySourceGen
+ ? "null"
+ : $"this.{keyTypeReadableName}";
+ }
+
+ // Value metadata
+ TypeMetadata? collectionValueTypeMetadata = typeMetadata.CollectionValueTypeMetadata;
+ Debug.Assert(collectionValueTypeMetadata != null);
+ string valueTypeCompilableName = collectionValueTypeMetadata.CompilableName;
+ string valueTypeReadableName = collectionValueTypeMetadata.FriendlyName;
+
+ string valueTypeMetadataPropertyName = collectionValueTypeMetadata.ClassType == ClassType.TypeUnsupportedBySourceGen
+ ? "null"
+ : $"this.{valueTypeReadableName}";
+
+ string numberHandlingArg = $"{GetNumberHandlingAsStr(typeMetadata.NumberHandling)}";
+
+ CollectionType collectionType = typeMetadata.CollectionType;
+ string collectionTypeInfoValue = collectionType switch
+ {
+ CollectionType.Array => $"{JsonMetadataServicesClassName}.CreateArrayInfo<{valueTypeCompilableName}>({OptionsInstanceVariableName}, {valueTypeMetadataPropertyName}, {numberHandlingArg})",
+ CollectionType.List => $"{JsonMetadataServicesClassName}.CreateListInfo<{typeCompilableName}, {valueTypeCompilableName}>({OptionsInstanceVariableName}, () => new System.Collections.Generic.List<{valueTypeCompilableName}>(), {valueTypeMetadataPropertyName}, {numberHandlingArg})",
+ CollectionType.Dictionary => $"{JsonMetadataServicesClassName}.CreateDictionaryInfo<{typeCompilableName}, {keyTypeCompilableName!}, {valueTypeCompilableName}>({OptionsInstanceVariableName}, () => new System.Collections.Generic.Dictionary<{keyTypeCompilableName}, {valueTypeCompilableName}>(), {keyTypeMetadataPropertyName!}, {valueTypeMetadataPropertyName}, {numberHandlingArg})",
+ _ => throw new NotSupportedException()
+ };
+
+ string metadataInitSource = @$"_{typeFriendlyName} = {collectionTypeInfoValue};";
+ return GenerateForType(typeMetadata, metadataInitSource);
+ }
+
+ private string GenerateForObject(TypeMetadata typeMetadata)
+ {
+ string typeCompilableName = typeMetadata.CompilableName;
+ string typeFriendlyName = typeMetadata.FriendlyName;
+
+ string createObjectFuncTypeArg = typeMetadata.ConstructionStrategy == ObjectConstructionStrategy.ParameterlessConstructor
+ ? $"createObjectFunc: static () => new {typeMetadata.CompilableName}()"
+ : "createObjectFunc: null";
+
+ List? properties = typeMetadata.PropertiesMetadata;
+
+ StringBuilder sb = new();
+
+ sb.Append($@"JsonTypeInfo<{typeCompilableName}> objectInfo = {JsonMetadataServicesClassName}.CreateObjectInfo<{typeCompilableName}>();
+ _{typeFriendlyName} = objectInfo;
+");
+
+ string propInitFuncVarName = $"{typeFriendlyName}{PropInitFuncVarName}";
+
+ sb.Append($@"
+ {JsonMetadataServicesClassName}.InitializeObjectInfo(
+ objectInfo,
+ {OptionsInstanceVariableName},
+ {createObjectFuncTypeArg},
+ {propInitFuncVarName},
+ {GetNumberHandlingAsStr(typeMetadata.NumberHandling)});");
+
+ string metadataInitSource = sb.ToString();
+ string? propInitFuncSource = GeneratePropMetadataInitFunc(typeMetadata.IsValueType, propInitFuncVarName, properties);
+
+ return GenerateForType(typeMetadata, metadataInitSource, propInitFuncSource);
+ }
+
+ private string GeneratePropMetadataInitFunc(
+ bool declaringTypeIsValueType,
+ string propInitFuncVarName,
+ List? properties)
+ {
+ const string PropVarName = "properties";
+ const string JsonContextVarName = "jsonContext";
+ const string JsonPropertyInfoTypeName = "JsonPropertyInfo";
+
+ string propertyArrayInstantiationValue = properties == null
+ ? $"System.Array.Empty<{JsonPropertyInfoTypeName}>()"
+ : $"new {JsonPropertyInfoTypeName}[{properties.Count}]";
+
+ StringBuilder sb = new();
+
+ sb.Append($@"
+ private static {JsonPropertyInfoTypeName}[] {propInitFuncVarName}(JsonSerializerContext context)
+ {{
+ JsonContext {JsonContextVarName} = (JsonContext)context;
+ JsonSerializerOptions options = context.Options;
+
+ {JsonPropertyInfoTypeName}[] {PropVarName} = {propertyArrayInstantiationValue};
+");
+
+ if (properties != null)
+ {
+ for (int i = 0; i < properties.Count; i++)
+ {
+ PropertyMetadata memberMetadata = properties[i];
+
+ TypeMetadata memberTypeMetadata = memberMetadata.TypeMetadata;
+
+ string clrPropertyName = memberMetadata.ClrName;
+
+ string declaringTypeCompilableName = memberMetadata.DeclaringTypeCompilableName;
+
+ string memberTypeFriendlyName = memberTypeMetadata.ClassType == ClassType.TypeUnsupportedBySourceGen
+ ? "null"
+ : $"{JsonContextVarName}.{memberTypeMetadata.FriendlyName}";
+
+ string typeTypeInfoNamedArg = $"propertyTypeInfo: {memberTypeFriendlyName}";
+
+ string jsonPropertyNameNamedArg = memberMetadata.JsonPropertyName != null
+ ? @$"jsonPropertyName: ""{memberMetadata.JsonPropertyName}"""
+ : "jsonPropertyName: null";
+
+ string getterNamedArg = memberMetadata.HasGetter
+ ? $"getter: static (obj) => {{ return (({declaringTypeCompilableName})obj).{clrPropertyName}; }}"
+ : "getter: null";
+
+ string setterNamedArg;
+ if (memberMetadata.HasSetter)
+ {
+ string propMutation = declaringTypeIsValueType
+ ? @$"{{ Unsafe.Unbox<{declaringTypeCompilableName}>(obj).{clrPropertyName} = value; }}"
+ : $@"{{ (({declaringTypeCompilableName})obj).{clrPropertyName} = value; }}";
+
+ setterNamedArg = $"setter: static (obj, value) => {propMutation}";
+ }
+ else
+ {
+ setterNamedArg = "setter: null";
+ }
+
+ JsonIgnoreCondition? ignoreCondition = memberMetadata.IgnoreCondition;
+ string ignoreConditionNamedArg = ignoreCondition.HasValue
+ ? $"ignoreCondition: JsonIgnoreCondition.{ignoreCondition.Value}"
+ : "ignoreCondition: default";
+
+ string converterNamedArg = memberMetadata.ConverterInstantiationLogic == null
+ ? "converter: null"
+ : $"converter: {memberMetadata.ConverterInstantiationLogic}";
+
+ string memberTypeCompilableName = memberTypeMetadata.CompilableName;
+
+ sb.Append($@"
+ {PropVarName}[{i}] = {JsonMetadataServicesClassName}.CreatePropertyInfo<{memberTypeCompilableName}>(
+ options,
+ isProperty: {memberMetadata.IsProperty.ToString().ToLowerInvariant()},
+ declaringType: typeof({memberMetadata.DeclaringTypeCompilableName}),
+ {typeTypeInfoNamedArg},
+ {converterNamedArg},
+ {getterNamedArg},
+ {setterNamedArg},
+ {ignoreConditionNamedArg},
+ numberHandling: {GetNumberHandlingAsStr(memberMetadata.NumberHandling)},
+ propertyName: ""{clrPropertyName}"",
+ {jsonPropertyNameNamedArg});
+ ");
+ }
+ }
+
+ sb.Append(@$"
+ return {PropVarName};
+ }}");
+
+ return sb.ToString();
+ }
+
+ private string GenerateForType(TypeMetadata typeMetadata, string metadataInitSource, string? additionalSource = null)
+ {
+ string typeCompilableName = typeMetadata.CompilableName;
+ string typeFriendlyName = typeMetadata.FriendlyName;
+
+ return @$"{GetUsingStatementsString(typeMetadata)}
+
+namespace {_generationNamespace}
+{{
+ {JsonContextDeclarationSource}
+ {{
+ private JsonTypeInfo<{typeCompilableName}> _{typeFriendlyName};
+ public JsonTypeInfo<{typeCompilableName}> {typeFriendlyName}
+ {{
+ get
+ {{
+ if (_{typeFriendlyName} == null)
+ {{
+ {WrapWithCheckForCustomConverterIfRequired(metadataInitSource, typeCompilableName, typeFriendlyName, GetNumberHandlingAsStr(typeMetadata.NumberHandling))}
+ }}
+
+ return _{typeFriendlyName};
+ }}
+ }}{additionalSource}
+ }}
+}}
+";
+ }
+
+ private string WrapWithCheckForCustomConverterIfRequired(string source, string typeCompilableName, string typeFriendlyName, string numberHandlingNamedArg)
+ {
+ if (!_honorRuntimeProvidedCustomConverters)
+ {
+ return source;
+ }
+
+ return @$"JsonConverter customConverter;
+ if ({OptionsInstanceVariableName}.Converters.Count > 0 && (customConverter = {RuntimeCustomConverterFetchingMethodName}(typeof({typeCompilableName}))) != null)
+ {{
+ _{typeFriendlyName} = {JsonMetadataServicesClassName}.{GetCreateValueInfoMethodRef(typeCompilableName)}({OptionsInstanceVariableName}, customConverter);
+ }}
+ else
+ {{
+ {source.Replace(Environment.NewLine, $"{Environment.NewLine} ")}
+ }}";
+ }
+
+ private string GetBaseJsonContextImplementation()
+ {
+ StringBuilder sb = new();
+ sb.Append(@$"using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace {_generationNamespace}
+{{
+ {JsonContextDeclarationSource}
+ {{
+ private static JsonContext s_default;
+ public static JsonContext Default => s_default ??= new JsonContext(new JsonSerializerOptions());
+
+ public JsonContext() : base(null)
+ {{
+ }}
+
+ public JsonContext(JsonSerializerOptions options) : base(options)
+ {{
+ }}
+
+ {GetFetchLogicForRuntimeSpecifiedCustomConverter()}
+ }}
+}}
+");
+
+ return sb.ToString();
+ }
+
+ private string GetFetchLogicForRuntimeSpecifiedCustomConverter()
+ {
+ if (!_honorRuntimeProvidedCustomConverters)
+ {
+ return "";
+ }
+
+ // TODO (https://github.com/dotnet/runtime/issues/52218): use a dictionary if count > ~15.
+ return @$"private JsonConverter {RuntimeCustomConverterFetchingMethodName}(System.Type type)
+ {{
+ System.Collections.Generic.IList converters = {OptionsInstanceVariableName}.Converters;
+
+ for (int i = 0; i < converters.Count; i++)
+ {{
+ JsonConverter converter = converters[i];
+
+ if (converter.CanConvert(type))
+ {{
+ if (converter is JsonConverterFactory factory)
+ {{
+ converter = factory.CreateConverter(type, {OptionsInstanceVariableName});
+ if (converter == null || converter is JsonConverterFactory)
+ {{
+ throw new System.InvalidOperationException($""The converter '{{factory.GetType()}}' cannot return null or a JsonConverterFactory instance."");
+ }}
+ }}
+
+ return converter;
+ }}
+ }}
+
+ return null;
+ }}";
+ }
+
+ private string GetGetTypeInfoImplementation()
+ {
+ StringBuilder sb = new();
+
+ HashSet usingStatements = new();
+
+ foreach (TypeMetadata metadata in _rootSerializableTypes.Values)
+ {
+ usingStatements.UnionWith(GetUsingStatements(metadata));
+ }
+
+ sb.Append(@$"{GetUsingStatementsString(usingStatements)}
+
+namespace {_generationNamespace}
+{{
+ {JsonContextDeclarationSource}
+ {{
+ public override JsonTypeInfo GetTypeInfo(System.Type type)
+ {{");
+
+ // TODO (https://github.com/dotnet/runtime/issues/52218): Make this Dictionary-lookup-based if root-serializable type count > 64.
+ foreach (TypeMetadata metadata in _rootSerializableTypes.Values)
+ {
+ if (metadata.ClassType != ClassType.TypeUnsupportedBySourceGen)
+ {
+ sb.Append($@"
+ if (type == typeof({metadata.Type.GetUniqueCompilableTypeName()}))
+ {{
+ return this.{metadata.FriendlyName};
+ }}
+");
+ }
+ }
+
+ sb.Append(@"
+ return null!;
+ }
+ }
+}
+");
+
+ return sb.ToString();
+ }
+
+ private static string GetUsingStatementsString(TypeMetadata typeMetadata)
+ {
+ HashSet usingStatements = GetUsingStatements(typeMetadata);
+ return GetUsingStatementsString(usingStatements);
+ }
+
+ private static string GetUsingStatementsString(HashSet usingStatements)
+ {
+ string[] usingsArr = usingStatements.ToArray();
+ Array.Sort(usingsArr);
+ return string.Join("\n", usingsArr);
+ }
+
+ private static HashSet GetUsingStatements(TypeMetadata typeMetadata)
+ {
+ HashSet usingStatements = new();
+
+ // Add library usings.
+ usingStatements.Add(FormatAsUsingStatement("System.Runtime.CompilerServices"));
+ usingStatements.Add(FormatAsUsingStatement("System.Text.Json"));
+ usingStatements.Add(FormatAsUsingStatement("System.Text.Json.Serialization"));
+ usingStatements.Add(FormatAsUsingStatement("System.Text.Json.Serialization.Metadata"));
+
+ // Add imports to root type.
+ usingStatements.Add(FormatAsUsingStatement(typeMetadata.Type.Namespace));
+
+ switch (typeMetadata.ClassType)
+ {
+ case ClassType.Nullable:
+ {
+ AddUsingStatementsForType(typeMetadata.NullableUnderlyingTypeMetadata!);
+ }
+ break;
+ case ClassType.Enumerable:
+ {
+ AddUsingStatementsForType(typeMetadata.CollectionValueTypeMetadata);
+ }
+ break;
+ case ClassType.Dictionary:
+ {
+ AddUsingStatementsForType(typeMetadata.CollectionKeyTypeMetadata);
+ AddUsingStatementsForType(typeMetadata.CollectionValueTypeMetadata);
+ }
+ break;
+ case ClassType.Object:
+ {
+ if (typeMetadata.PropertiesMetadata != null)
+ {
+ foreach (PropertyMetadata metadata in typeMetadata.PropertiesMetadata)
+ {
+ AddUsingStatementsForType(metadata.TypeMetadata);
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ void AddUsingStatementsForType(TypeMetadata typeMetadata)
+ {
+ usingStatements.Add(FormatAsUsingStatement(typeMetadata.Type.Namespace));
+
+ if (typeMetadata.CollectionKeyTypeMetadata != null)
+ {
+ Debug.Assert(typeMetadata.CollectionValueTypeMetadata != null);
+ usingStatements.Add(FormatAsUsingStatement(typeMetadata.CollectionKeyTypeMetadata.Type.Namespace));
+ }
+
+ if (typeMetadata.CollectionValueTypeMetadata != null)
+ {
+ usingStatements.Add(FormatAsUsingStatement(typeMetadata.CollectionValueTypeMetadata.Type.Namespace));
+ }
+ }
+
+ return usingStatements;
+ }
+
+ private static string FormatAsUsingStatement(string @namespace) => $"using {@namespace};";
+
+ private static string GetNumberHandlingAsStr(JsonNumberHandling? numberHandling) =>
+ numberHandling.HasValue
+ ? $"(JsonNumberHandling){(int)numberHandling.Value}"
+ : "default";
+
+ private static string GetCreateValueInfoMethodRef(string typeCompilableName) => $"{CreateValueInfoMethodName}<{typeCompilableName}>";
+ }
+ }
+}
diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
new file mode 100644
index 0000000000000..86a7043b67f18
--- /dev/null
+++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
@@ -0,0 +1,517 @@
+// 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;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Reflection;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Text;
+using System.Text.Json.Serialization;
+using System.Text.Json.SourceGeneration.Reflection;
+using System.Linq;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace System.Text.Json.SourceGeneration
+{
+ public sealed partial class JsonSourceGenerator
+ {
+ private sealed class Parser
+ {
+ private const string SystemTextJsonNamespace = "System.Text.Json";
+
+ private const string JsonConverterAttributeFullName = "System.Text.Json.Serialization.JsonConverterAttribute";
+
+ private const string JsonIgnoreAttributeFullName = "System.Text.Json.Serialization.JsonIgnoreAttribute";
+
+ private const string JsonIgnoreConditionFullName = "System.Text.Json.Serialization.JsonIgnoreCondition";
+
+ private const string JsonIncludeAttributeFullName = "System.Text.Json.Serialization.JsonIncludeAttribute";
+
+ private const string JsonNumberHandlingAttributeFullName = "System.Text.Json.Serialization.JsonNumberHandlingAttribute";
+
+ private const string JsonPropertyNameAttributeFullName = "System.Text.Json.Serialization.JsonPropertyNameAttribute";
+
+ private readonly Compilation _compilation;
+
+ private readonly MetadataLoadContextInternal _metadataLoadContext;
+
+ private readonly Type _ienumerableType;
+ private readonly Type _listOfTType;
+ private readonly Type _dictionaryType;
+
+ private readonly Type _booleanType;
+ private readonly Type _byteArrayType;
+ private readonly Type _charType;
+ private readonly Type _dateTimeType;
+ private readonly Type _dateTimeOffsetType;
+ private readonly Type _guidType;
+ private readonly Type _stringType;
+ private readonly Type _uriType;
+ private readonly Type _versionType;
+
+ private readonly HashSet _numberTypes = new();
+
+ private readonly HashSet _knownTypes = new();
+
+ ///
+ /// Type information for member types in input object graphs.
+ ///
+ private readonly Dictionary _typeMetadataCache = new();
+
+ public Parser(Compilation compilation)
+ {
+ _compilation = compilation;
+ _metadataLoadContext = new MetadataLoadContextInternal(compilation);
+
+ _ienumerableType = _metadataLoadContext.Resolve(typeof(IEnumerable));
+ _listOfTType = _metadataLoadContext.Resolve(typeof(List<>));
+ _dictionaryType = _metadataLoadContext.Resolve(typeof(Dictionary<,>));
+
+ _booleanType = _metadataLoadContext.Resolve(typeof(bool));
+ _byteArrayType = _metadataLoadContext.Resolve(typeof(byte[]));
+ _charType = _metadataLoadContext.Resolve(typeof(char));
+ _dateTimeType = _metadataLoadContext.Resolve(typeof(DateTime));
+ _dateTimeOffsetType = _metadataLoadContext.Resolve(typeof(DateTimeOffset));
+ _guidType = _metadataLoadContext.Resolve(typeof(Guid));
+ _stringType = _metadataLoadContext.Resolve(typeof(string));
+ _uriType = _metadataLoadContext.Resolve(typeof(Uri));
+ _versionType = _metadataLoadContext.Resolve(typeof(Version));
+
+ PopulateKnownTypes();
+ }
+
+ public Dictionary? GetRootSerializableTypes(List compilationUnits)
+ {
+ TypeExtensions.NullableOfTType = _metadataLoadContext.Resolve(typeof(Nullable<>));
+
+ const string JsonSerializableAttributeName = "System.Text.Json.Serialization.JsonSerializableAttribute";
+ INamedTypeSymbol jsonSerializableAttribute = _compilation.GetTypeByMetadataName(JsonSerializableAttributeName);
+ if (jsonSerializableAttribute == null)
+ {
+ return null;
+ }
+
+ // Discover serializable types indicated by JsonSerializableAttribute.
+ Dictionary? rootTypes = null;
+
+ foreach (CompilationUnitSyntax compilationUnit in compilationUnits)
+ {
+ SemanticModel compilationSemanticModel = _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 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 == "")
+ {
+ // 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 null;
+ }
+
+ rootTypes ??= new Dictionary();
+ rootTypes[type.FullName] = GetOrAddTypeMetadata(type, typeInfoPropertyName);
+ }
+ }
+
+ return rootTypes;
+ }
+
+ private TypeMetadata GetOrAddTypeMetadata(Type type, string? typeInfoPropertyName = null)
+ {
+ if (_typeMetadataCache.TryGetValue(type, out TypeMetadata? typeMetadata))
+ {
+ return typeMetadata!;
+ }
+
+ // Add metadata to cache now to prevent stack overflow when the same type is found somewhere else in the object graph.
+ typeMetadata = new();
+ _typeMetadataCache[type] = typeMetadata;
+
+ ClassType classType;
+ Type? collectionKeyType = null;
+ Type? collectionValueType = null;
+ Type? nullableUnderlyingType = null;
+ List? propertiesMetadata = null;
+ CollectionType collectionType = CollectionType.NotApplicable;
+ ObjectConstructionStrategy constructionStrategy = default;
+ JsonNumberHandling? numberHandling = null;
+ bool containsOnlyPrimitives = true;
+
+ bool foundDesignTimeCustomConverter = false;
+ string? converterInstatiationLogic = null;
+
+ IList attributeDataList = CustomAttributeData.GetCustomAttributes(type);
+ foreach (CustomAttributeData attributeData in attributeDataList)
+ {
+ Type attributeType = attributeData.AttributeType;
+ if (attributeType.FullName == "System.Text.Json.Serialization.JsonNumberHandlingAttribute")
+ {
+ IList ctorArgs = attributeData.ConstructorArguments;
+ numberHandling = (JsonNumberHandling)ctorArgs[0].Value;
+ continue;
+ }
+ else if (!foundDesignTimeCustomConverter && attributeType.GetCompatibleBaseClass(JsonConverterAttributeFullName) != null)
+ {
+ foundDesignTimeCustomConverter = true;
+ converterInstatiationLogic = GetConverterInstantiationLogic(attributeData);
+ }
+ }
+
+ if (foundDesignTimeCustomConverter)
+ {
+ classType = converterInstatiationLogic != null
+ ? ClassType.TypeWithDesignTimeProvidedCustomConverter
+ : ClassType.TypeUnsupportedBySourceGen;
+ }
+ else if (_knownTypes.Contains(type))
+ {
+ classType = ClassType.KnownType;
+ }
+ else if (type.IsNullableValueType(out nullableUnderlyingType))
+ {
+ Debug.Assert(nullableUnderlyingType != null);
+ classType = ClassType.Nullable;
+ }
+ else if (type.IsEnum)
+ {
+ classType = ClassType.Enum;
+ }
+ else if (_ienumerableType.IsAssignableFrom(type))
+ {
+ // Only T[], List, and Dictionary are supported.
+
+ if (type.IsArray)
+ {
+ classType = ClassType.Enumerable;
+ collectionType = CollectionType.Array;
+ collectionValueType = type.GetElementType();
+ }
+ else if (!type.IsGenericType)
+ {
+ classType = ClassType.TypeUnsupportedBySourceGen;
+ }
+ else
+ {
+ Type genericTypeDef = type.GetGenericTypeDefinition();
+ Type[] genericTypeArgs = type.GetGenericArguments();
+
+ if (genericTypeDef == _listOfTType)
+ {
+ classType = ClassType.Enumerable;
+ collectionType = CollectionType.List;
+ collectionValueType = genericTypeArgs[0];
+ }
+ else if (genericTypeDef == _dictionaryType)
+ {
+ classType = ClassType.Dictionary;
+ collectionType = CollectionType.Dictionary;
+ collectionKeyType = genericTypeArgs[0];
+ collectionValueType = genericTypeArgs[1];
+ }
+ else
+ {
+ classType = ClassType.TypeUnsupportedBySourceGen;
+ }
+ }
+ }
+ else
+ {
+ classType = ClassType.Object;
+
+ if (type.GetConstructor(Type.EmptyTypes) != null && !type.IsAbstract && !type.IsInterface)
+ {
+ constructionStrategy = ObjectConstructionStrategy.ParameterlessConstructor;
+ }
+
+ for (Type? currentType = type; currentType != null; currentType = currentType.BaseType)
+ {
+ const BindingFlags bindingFlags =
+ BindingFlags.Instance |
+ BindingFlags.Public |
+ BindingFlags.NonPublic |
+ BindingFlags.DeclaredOnly;
+
+ foreach (PropertyInfo propertyInfo in currentType.GetProperties(bindingFlags))
+ {
+ PropertyMetadata metadata = GetPropertyMetadata(propertyInfo);
+
+ // Ignore indexers.
+ if (propertyInfo.GetIndexParameters().Length > 0)
+ {
+ continue;
+ }
+
+ string key = metadata.JsonPropertyName ?? metadata.ClrName;
+
+ if (metadata.HasGetter || metadata.HasSetter)
+ {
+ (propertiesMetadata ??= new()).Add(metadata);
+ }
+
+ if (containsOnlyPrimitives && !IsPrimitive(propertyInfo.PropertyType))
+ {
+ containsOnlyPrimitives = false;
+ }
+ }
+
+ foreach (FieldInfo fieldInfo in currentType.GetFields(bindingFlags))
+ {
+ PropertyMetadata metadata = GetPropertyMetadata(fieldInfo);
+
+ if (metadata.HasGetter || metadata.HasSetter)
+ {
+ (propertiesMetadata ??= new()).Add(metadata);
+ }
+ }
+ }
+ }
+
+ typeMetadata.Initialize(
+ compilableName: type.GetUniqueCompilableTypeName(),
+ friendlyName: typeInfoPropertyName ?? type.GetFriendlyTypeName(),
+ type,
+ classType,
+ isValueType: type.IsValueType,
+ numberHandling,
+ propertiesMetadata,
+ collectionType,
+ collectionKeyTypeMetadata: collectionKeyType != null ? GetOrAddTypeMetadata(collectionKeyType) : null,
+ collectionValueTypeMetadata: collectionValueType != null ? GetOrAddTypeMetadata(collectionValueType) : null,
+ constructionStrategy,
+ nullableUnderlyingTypeMetadata: nullableUnderlyingType != null ? GetOrAddTypeMetadata(nullableUnderlyingType) : null,
+ converterInstatiationLogic,
+ containsOnlyPrimitives);
+
+ return typeMetadata;
+ }
+
+ private PropertyMetadata GetPropertyMetadata(MemberInfo memberInfo)
+ {
+ IList attributeDataList = CustomAttributeData.GetCustomAttributes(memberInfo);
+
+ bool hasJsonInclude = false;
+ JsonIgnoreCondition? ignoreCondition = null;
+ JsonNumberHandling? numberHandling = null;
+ string? jsonPropertyName = null;
+
+ bool foundDesignTimeCustomConverter = false;
+ string? converterInstantiationLogic = null;
+
+ foreach (CustomAttributeData attributeData in attributeDataList)
+ {
+ Type attributeType = attributeData.AttributeType;
+
+ if (!foundDesignTimeCustomConverter && attributeType.GetCompatibleBaseClass(JsonConverterAttributeFullName) != null)
+ {
+ foundDesignTimeCustomConverter = true;
+ converterInstantiationLogic = GetConverterInstantiationLogic(attributeData);
+ }
+ else if (attributeType.Assembly.FullName == SystemTextJsonNamespace)
+ {
+ switch (attributeData.AttributeType.FullName)
+ {
+ case JsonIgnoreAttributeFullName:
+ {
+ IList namedArgs = attributeData.NamedArguments;
+
+ if (namedArgs.Count == 0)
+ {
+ ignoreCondition = JsonIgnoreCondition.Always;
+ }
+ else if (namedArgs.Count == 1 &&
+ namedArgs[0].MemberInfo.MemberType == MemberTypes.Property &&
+ ((PropertyInfo)namedArgs[0].MemberInfo).PropertyType.FullName == JsonIgnoreConditionFullName)
+ {
+ ignoreCondition = (JsonIgnoreCondition)namedArgs[0].TypedValue.Value;
+ }
+ }
+ break;
+ case JsonIncludeAttributeFullName:
+ {
+ hasJsonInclude = true;
+ }
+ break;
+ case JsonNumberHandlingAttributeFullName:
+ {
+ IList ctorArgs = attributeData.ConstructorArguments;
+ numberHandling = (JsonNumberHandling)ctorArgs[0].Value;
+ }
+ break;
+ case JsonPropertyNameAttributeFullName:
+ {
+ IList ctorArgs = attributeData.ConstructorArguments;
+ jsonPropertyName = (string)ctorArgs[0].Value;
+ // Null check here is done at runtime within JsonSerializer.
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ Type memberCLRType;
+ bool hasGetter;
+ bool hasSetter;
+ bool getterIsVirtual = false;
+ bool setterIsVirtual = false;
+
+ switch (memberInfo)
+ {
+ case PropertyInfo propertyInfo:
+ {
+ MethodInfo setMethod = propertyInfo.SetMethod;
+
+ memberCLRType = propertyInfo.PropertyType;
+ hasGetter = PropertyAccessorCanBeReferenced(propertyInfo.GetMethod, hasJsonInclude);
+ hasSetter = PropertyAccessorCanBeReferenced(setMethod, hasJsonInclude) && !setMethod.IsInitOnly();
+ getterIsVirtual = propertyInfo.GetMethod?.IsVirtual == true;
+ setterIsVirtual = propertyInfo.SetMethod?.IsVirtual == true;
+ }
+ break;
+ case FieldInfo fieldInfo:
+ {
+ Debug.Assert(fieldInfo.IsPublic);
+
+ memberCLRType = fieldInfo.FieldType;
+ hasGetter = true;
+ hasSetter = !fieldInfo.IsInitOnly;
+ }
+ break;
+ default:
+ throw new InvalidOperationException();
+ }
+
+ return new PropertyMetadata
+ {
+ ClrName = memberInfo.Name,
+ IsProperty = memberInfo.MemberType == MemberTypes.Property,
+ JsonPropertyName = jsonPropertyName,
+ HasGetter = hasGetter,
+ HasSetter = hasSetter,
+ GetterIsVirtual = getterIsVirtual,
+ SetterIsVirtual = setterIsVirtual,
+ IgnoreCondition = ignoreCondition,
+ NumberHandling = numberHandling,
+ HasJsonInclude = hasJsonInclude,
+ TypeMetadata = GetOrAddTypeMetadata(memberCLRType),
+ DeclaringTypeCompilableName = memberInfo.DeclaringType.GetUniqueCompilableTypeName(),
+ ConverterInstantiationLogic = converterInstantiationLogic
+ };
+ }
+
+ private static bool PropertyAccessorCanBeReferenced(MethodInfo? memberAccessor, bool hasJsonInclude) =>
+ (memberAccessor != null && !memberAccessor.IsPrivate) && (memberAccessor.IsPublic || hasJsonInclude);
+
+ private string? GetConverterInstantiationLogic(CustomAttributeData attributeData)
+ {
+ if (attributeData.AttributeType.FullName != JsonConverterAttributeFullName)
+ {
+ return null;
+ }
+
+ Type converterType = new TypeWrapper((ITypeSymbol)attributeData.ConstructorArguments[0].Value, _metadataLoadContext);
+
+ if (converterType == null || converterType.GetConstructor(Type.EmptyTypes) == null || converterType.IsNestedPrivate)
+ {
+ return null;
+ }
+
+ return $"new {converterType.GetUniqueCompilableTypeName()}()";
+ }
+
+ private void PopulateNumberTypes()
+ {
+ Debug.Assert(_numberTypes != null);
+ _numberTypes.Add(_metadataLoadContext.Resolve(typeof(byte)));
+ _numberTypes.Add(_metadataLoadContext.Resolve(typeof(decimal)));
+ _numberTypes.Add(_metadataLoadContext.Resolve(typeof(double)));
+ _numberTypes.Add(_metadataLoadContext.Resolve(typeof(short)));
+ _numberTypes.Add(_metadataLoadContext.Resolve(typeof(sbyte)));
+ _numberTypes.Add(_metadataLoadContext.Resolve(typeof(int)));
+ _numberTypes.Add(_metadataLoadContext.Resolve(typeof(long)));
+ _numberTypes.Add(_metadataLoadContext.Resolve(typeof(float)));
+ _numberTypes.Add(_metadataLoadContext.Resolve(typeof(ushort)));
+ _numberTypes.Add(_metadataLoadContext.Resolve(typeof(uint)));
+ _numberTypes.Add(_metadataLoadContext.Resolve(typeof(ulong)));
+ }
+
+ private void PopulateKnownTypes()
+ {
+ PopulateNumberTypes();
+
+ Debug.Assert(_knownTypes != null);
+ Debug.Assert(_numberTypes != null);
+
+ _knownTypes.UnionWith(_numberTypes);
+ _knownTypes.Add(_booleanType);
+ _knownTypes.Add(_byteArrayType);
+ _knownTypes.Add(_charType);
+ _knownTypes.Add(_dateTimeType);
+ _knownTypes.Add(_dateTimeOffsetType);
+ _knownTypes.Add(_guidType);
+ _knownTypes.Add(_metadataLoadContext.Resolve(typeof(object)));
+ _knownTypes.Add(_stringType);
+
+ // System.Private.Uri may not be loaded in input compilation.
+ if (_uriType != null)
+ {
+ _knownTypes.Add(_uriType);
+ }
+
+ _knownTypes.Add(_metadataLoadContext.Resolve(typeof(Version)));
+ }
+
+ private bool IsPrimitive(Type type)
+ => _knownTypes.Contains(type) && type != _uriType && type != _versionType;
+ }
+ }
+}
diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs
index ef2412f6aaf90..eb23225f37f29 100644
--- a/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs
+++ b/src/libraries/System.Text.Json/gen/JsonSourceGenerator.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text.Json.SourceGeneration.Reflection;
@@ -15,14 +16,13 @@ namespace System.Text.Json.SourceGeneration
/// Generates source code to optimize serialization and deserialization with JsonSerializer.
///
[Generator]
- public sealed class JsonSourceGenerator : ISourceGenerator
+ public sealed partial class JsonSourceGenerator : ISourceGenerator
{
- private JsonSourceGeneratorHelper? _helper;
-
///
/// Helper for unit tests.
///
- public Dictionary? SerializableTypes => _helper.GetSerializableTypes();
+ public Dictionary? GetSerializableTypes() => _rootTypes?.ToDictionary(p => p.Key, p => p.Value.Type);
+ private Dictionary? _rootTypes;
///
/// Registers a syntax resolver to receive compilation units.
@@ -30,7 +30,7 @@ public sealed class JsonSourceGenerator : ISourceGenerator
///
public void Initialize(GeneratorInitializationContext context)
{
- context.RegisterForSyntaxNotifications(() => new JsonSerializableSyntaxReceiver());
+ context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
}
///
@@ -39,90 +39,34 @@ public void Initialize(GeneratorInitializationContext context)
///
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)
+ SyntaxReceiver receiver = (SyntaxReceiver)executionContext.SyntaxReceiver;
+ List compilationUnits = receiver.CompilationUnits;
+ if (compilationUnits == null)
{
return;
}
- JsonSerializableSyntaxReceiver receiver = (JsonSerializableSyntaxReceiver)executionContext.SyntaxReceiver;
- MetadataLoadContextInternal metadataLoadContext = new(compilation);
+ Parser parser = new(executionContext.Compilation);
+ _rootTypes = parser.GetRootSerializableTypes(receiver.CompilationUnits);
- TypeExtensions.NullableOfTType = metadataLoadContext.Resolve(typeof(Nullable<>));
+ if (_rootTypes != null)
+ {
+ Emitter emitter = new(executionContext, _rootTypes);
+ emitter.Emit();
+ }
+ }
- JsonSourceGeneratorHelper helper = new(executionContext, metadataLoadContext);
- _helper = helper;
+ internal sealed class SyntaxReceiver : ISyntaxReceiver
+ {
+ public List? CompilationUnits { get; private set; }
- // Discover serializable types indicated by JsonSerializableAttribute.
- foreach (CompilationUnitSyntax compilationUnit in receiver.CompilationUnits)
+ public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
- SemanticModel compilationSemanticModel = executionContext.Compilation.GetSemanticModel(compilationUnit.SyntaxTree);
-
- foreach (AttributeListSyntax attributeListSyntax in compilationUnit.AttributeLists)
+ if (syntaxNode is CompilationUnitSyntax compilationUnit)
{
- 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 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 == "")
- {
- // 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);
+ (CompilationUnits ??= new List()).Add(compilationUnit);
}
}
-
- helper.GenerateSerializationMetadata();
}
}
}
diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGeneratorHelper.Generate.cs b/src/libraries/System.Text.Json/gen/JsonSourceGeneratorHelper.Generate.cs
deleted file mode 100644
index 040c11cf9acf0..0000000000000
--- a/src/libraries/System.Text.Json/gen/JsonSourceGeneratorHelper.Generate.cs
+++ /dev/null
@@ -1,651 +0,0 @@
-// 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.Diagnostics;
-using System.Linq;
-using System.Text.Json.Serialization;
-using System.Text.Json.SourceGeneration.Reflection;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.Text;
-
-namespace System.Text.Json.SourceGeneration
-{
- internal sealed partial class JsonSourceGeneratorHelper
- {
- private readonly string _generationNamespace;
-
- // TODO: consider public option for this.
- // Converter-honoring logic generation can be simplified
- // if we don't plan to have a feature around this.
- private bool _honorRuntimeProvidedCustomConverters = true;
-
- private const string RuntimeCustomConverterFetchingMethodName = "GetRuntimeProvidedCustomConverter";
-
- private const string JsonContextDeclarationSource = "internal partial class JsonContext : JsonSerializerContext";
-
- private const string OptionsInstanceVariableName = "Options";
-
- private const string PropInitFuncVarName = "PropInitFunc";
-
- private const string JsonMetadataServicesClassName = "JsonMetadataServices";
-
- private const string CreateValueInfoMethodName = "CreateValueInfo";
-
- private static DiagnosticDescriptor TypeNotSupported { get; } = new DiagnosticDescriptor(
- "SYSLIB2021",
- "Did not generate serialization metadata for type",
- "Did not generate serialization metadata for type {0}",
- "JSON source generation",
- DiagnosticSeverity.Warning,
- isEnabledByDefault: true,
- description: "Error message: {2}");
-
- private static DiagnosticDescriptor DuplicateTypeName { get; } = new DiagnosticDescriptor(
- "SYSLIB2022",
- "Duplicate type name",
- "There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision.",
- "JSON source generation",
- DiagnosticSeverity.Warning,
- isEnabledByDefault: true,
- description: "Error message: {2}");
-
- ///
- /// Types that we have initiated serialization metadata generation for. A type may be discoverable in the object graph,
- /// but not reachable for serialization (e.g. it is [JsonIgnore]'d); thus we maintain a separate cache.
- ///
- private readonly HashSet _typesWithMetadataGenerated = new();
-
- private void GenerateTypeMetadata(TypeMetadata typeMetadata)
- {
- Debug.Assert(typeMetadata != null);
-
- if (_typesWithMetadataGenerated.Contains(typeMetadata))
- {
- return;
- }
-
- _typesWithMetadataGenerated.Add(typeMetadata);
-
- string source;
-
- switch (typeMetadata.ClassType)
- {
- case ClassType.KnownType:
- {
- source = GenerateForTypeWithKnownConverter(typeMetadata);
- }
- break;
- case ClassType.TypeWithDesignTimeProvidedCustomConverter:
- {
- source = GenerateForTypeWithUnknownConverter(typeMetadata);
- }
- break;
- case ClassType.Nullable:
- {
- source = GenerateForNullable(typeMetadata);
-
- GenerateTypeMetadata(typeMetadata.NullableUnderlyingTypeMetadata);
- }
- break;
- case ClassType.Enum:
- {
- source = GenerateForEnum(typeMetadata);
- }
- break;
- case ClassType.Enumerable:
- {
- source = GenerateForCollection(typeMetadata);
-
- GenerateTypeMetadata(typeMetadata.CollectionValueTypeMetadata);
- }
- break;
- case ClassType.Dictionary:
- {
- source = GenerateForCollection(typeMetadata);
-
- GenerateTypeMetadata(typeMetadata.CollectionKeyTypeMetadata);
- GenerateTypeMetadata(typeMetadata.CollectionValueTypeMetadata);
- }
- break;
- case ClassType.Object:
- {
- source = GenerateForObject(typeMetadata);
-
- if (typeMetadata.PropertiesMetadata != null)
- {
- foreach (PropertyMetadata metadata in typeMetadata.PropertiesMetadata)
- {
- GenerateTypeMetadata(metadata.TypeMetadata);
- }
- }
- }
- break;
- case ClassType.TypeUnsupportedBySourceGen:
- {
- _executionContext.ReportDiagnostic(
- Diagnostic.Create(TypeNotSupported, Location.None, new string[] { typeMetadata.CompilableName }));
- return;
- }
- default:
- {
- throw new InvalidOperationException();
- }
- }
-
- try
- {
- _executionContext.AddSource($"{typeMetadata.FriendlyName}.cs", SourceText.From(source, Encoding.UTF8));
- }
- catch (ArgumentException)
- {
- _executionContext.ReportDiagnostic(Diagnostic.Create(DuplicateTypeName, Location.None, new string[] { typeMetadata.FriendlyName }));
- }
- }
-
- private string GenerateForTypeWithKnownConverter(TypeMetadata typeMetadata)
- {
- string typeCompilableName = typeMetadata.CompilableName;
- string typeFriendlyName = typeMetadata.FriendlyName;
-
- string metadataInitSource = $@"_{typeFriendlyName} = {JsonMetadataServicesClassName}.{GetCreateValueInfoMethodRef(typeCompilableName)}({OptionsInstanceVariableName}, {JsonMetadataServicesClassName}.{typeFriendlyName}Converter);";
-
- return GenerateForType(typeMetadata, metadataInitSource);
- }
-
- private string GenerateForTypeWithUnknownConverter(TypeMetadata typeMetadata)
- {
- string typeCompilableName = typeMetadata.CompilableName;
- string typeFriendlyName = typeMetadata.FriendlyName;
-
- StringBuilder sb = new();
-
- string metadataInitSource = $@"JsonConverter converter = {typeMetadata.ConverterInstantiationLogic};
- // TODO: consider moving this verification source to common helper.
- Type typeToConvert = typeof({typeCompilableName});
- if (!converter.CanConvert(typeToConvert))
- {{
- Type underlyingType = Nullable.GetUnderlyingType(typeToConvert);
- if (underlyingType != null && converter.CanConvert(underlyingType))
- {{
- JsonConverter actualConverter = converter;
-
- if (converter is JsonConverterFactory converterFactory)
- {{
- actualConverter = converterFactory.CreateConverter(underlyingType, {OptionsInstanceVariableName});
-
- if (actualConverter == null || actualConverter is JsonConverterFactory)
- {{
- throw new InvalidOperationException($""JsonConverterFactory '{{converter}} cannot return a 'null' or 'JsonConverterFactory' value."");
- }}
- }}
-
- // Allow nullable handling to forward to the underlying type's converter.
- converter = {JsonMetadataServicesClassName}.GetNullableConverter<{typeCompilableName}>((JsonConverter<{typeCompilableName}>)actualConverter);
- }}
- else
- {{
- throw new InvalidOperationException($""The converter '{{converter.GetType()}}' is not compatible with the type '{{typeToConvert}}'."");
- }}
- }}
-
- _{typeFriendlyName} = {JsonMetadataServicesClassName}.{GetCreateValueInfoMethodRef(typeCompilableName)}({OptionsInstanceVariableName}, converter);";
-
- return GenerateForType(typeMetadata, metadataInitSource);
- }
-
- private string GenerateForNullable(TypeMetadata typeMetadata)
- {
- string typeCompilableName = typeMetadata.CompilableName;
- string typeFriendlyName = typeMetadata.FriendlyName;
-
- TypeMetadata? underlyingTypeMetadata = typeMetadata.NullableUnderlyingTypeMetadata;
- Debug.Assert(underlyingTypeMetadata != null);
- string underlyingTypeCompilableName = underlyingTypeMetadata.CompilableName;
- string underlyingTypeFriendlyName = underlyingTypeMetadata.FriendlyName;
- string underlyingTypeInfoNamedArg = underlyingTypeMetadata.ClassType == ClassType.TypeUnsupportedBySourceGen
- ? "underlyingTypeInfo: null"
- : $"underlyingTypeInfo: {underlyingTypeFriendlyName}";
-
- string metadataInitSource = @$"_{typeFriendlyName} = {JsonMetadataServicesClassName}.{GetCreateValueInfoMethodRef(typeCompilableName)}(
- {OptionsInstanceVariableName},
- {JsonMetadataServicesClassName}.GetNullableConverter<{underlyingTypeCompilableName}>({underlyingTypeInfoNamedArg}));
-";
-
- return GenerateForType(typeMetadata, metadataInitSource);
- }
-
- private string GenerateForEnum(TypeMetadata typeMetadata)
- {
- string typeCompilableName = typeMetadata.CompilableName;
- string typeFriendlyName = typeMetadata.FriendlyName;
-
- string metadataInitSource = $"_{typeFriendlyName} = {JsonMetadataServicesClassName}.{GetCreateValueInfoMethodRef(typeCompilableName)}({OptionsInstanceVariableName}, JsonMetadataServices.GetEnumConverter<{typeCompilableName}>({OptionsInstanceVariableName}));";
-
- return GenerateForType(typeMetadata, metadataInitSource);
- }
-
- private string GenerateForCollection(TypeMetadata typeMetadata)
- {
- string typeCompilableName = typeMetadata.CompilableName;
- string typeFriendlyName = typeMetadata.FriendlyName;
-
- // Key metadata
- TypeMetadata? collectionKeyTypeMetadata = typeMetadata.CollectionKeyTypeMetadata;
- Debug.Assert(!(typeMetadata.CollectionType == CollectionType.Dictionary && collectionKeyTypeMetadata == null));
- string? keyTypeCompilableName = collectionKeyTypeMetadata?.CompilableName;
- string? keyTypeReadableName = collectionKeyTypeMetadata?.FriendlyName;
-
- string? keyTypeMetadataPropertyName;
- if (typeMetadata.ClassType != ClassType.Dictionary)
- {
- keyTypeMetadataPropertyName = "null";
- }
- else
- {
- keyTypeMetadataPropertyName = collectionKeyTypeMetadata.ClassType == ClassType.TypeUnsupportedBySourceGen
- ? "null"
- : $"this.{keyTypeReadableName}";
- }
-
- // Value metadata
- TypeMetadata? collectionValueTypeMetadata = typeMetadata.CollectionValueTypeMetadata;
- Debug.Assert(collectionValueTypeMetadata != null);
- string valueTypeCompilableName = collectionValueTypeMetadata.CompilableName;
- string valueTypeReadableName = collectionValueTypeMetadata.FriendlyName;
-
- string valueTypeMetadataPropertyName = collectionValueTypeMetadata.ClassType == ClassType.TypeUnsupportedBySourceGen
- ? "null"
- : $"this.{valueTypeReadableName}";
-
- string numberHandlingArg = $"{GetNumberHandlingAsStr(typeMetadata.NumberHandling)}";
-
- CollectionType collectionType = typeMetadata.CollectionType;
- string collectionTypeInfoValue = collectionType switch
- {
- CollectionType.Array => $"{JsonMetadataServicesClassName}.CreateArrayInfo<{valueTypeCompilableName}>({OptionsInstanceVariableName}, {valueTypeMetadataPropertyName}, {numberHandlingArg})",
- CollectionType.List => $"{JsonMetadataServicesClassName}.CreateListInfo<{typeCompilableName}, {valueTypeCompilableName}>({OptionsInstanceVariableName}, () => new System.Collections.Generic.List<{valueTypeCompilableName}>(), {valueTypeMetadataPropertyName}, {numberHandlingArg})",
- CollectionType.Dictionary => $"{JsonMetadataServicesClassName}.CreateDictionaryInfo<{typeCompilableName}, {keyTypeCompilableName!}, {valueTypeCompilableName}>({OptionsInstanceVariableName}, () => new System.Collections.Generic.Dictionary<{keyTypeCompilableName}, {valueTypeCompilableName}>(), {keyTypeMetadataPropertyName!}, {valueTypeMetadataPropertyName}, {numberHandlingArg})",
- _ => throw new NotSupportedException()
- };
-
- string metadataInitSource = @$"_{typeFriendlyName} = {collectionTypeInfoValue};";
- return GenerateForType(typeMetadata, metadataInitSource);
- }
-
- private string GenerateForObject(TypeMetadata typeMetadata)
- {
- string typeCompilableName = typeMetadata.CompilableName;
- string typeFriendlyName = typeMetadata.FriendlyName;
-
- string createObjectFuncTypeArg = typeMetadata.ConstructionStrategy == ObjectConstructionStrategy.ParameterlessConstructor
- ? $"createObjectFunc: static () => new {typeMetadata.CompilableName}()"
- : "createObjectFunc: null";
-
- List? properties = typeMetadata.PropertiesMetadata;
-
- StringBuilder sb = new();
-
- sb.Append($@"JsonTypeInfo<{typeCompilableName}> objectInfo = {JsonMetadataServicesClassName}.CreateObjectInfo<{typeCompilableName}>();
- _{typeFriendlyName} = objectInfo;
-");
-
- string propInitFuncVarName = $"{typeFriendlyName}{PropInitFuncVarName}";
-
- sb.Append($@"
- {JsonMetadataServicesClassName}.InitializeObjectInfo(
- objectInfo,
- {OptionsInstanceVariableName},
- {createObjectFuncTypeArg},
- {propInitFuncVarName},
- {GetNumberHandlingAsStr(typeMetadata.NumberHandling)});");
-
- string metadataInitSource = sb.ToString();
- string? propInitFuncSource = GeneratePropMetadataInitFunc(typeMetadata.IsValueType, propInitFuncVarName, properties);
-
- return GenerateForType(typeMetadata, metadataInitSource, propInitFuncSource);
- }
-
- private string GeneratePropMetadataInitFunc(
- bool declaringTypeIsValueType,
- string propInitFuncVarName,
- List? properties)
- {
- const string PropVarName = "properties";
- const string JsonContextVarName = "jsonContext";
- const string JsonPropertyInfoTypeName = "JsonPropertyInfo";
-
- string propertyArrayInstantiationValue = properties == null
- ? $"System.Array.Empty<{JsonPropertyInfoTypeName}>()"
- : $"new {JsonPropertyInfoTypeName}[{properties.Count}]";
-
- StringBuilder sb = new();
-
- sb.Append($@"
- private static {JsonPropertyInfoTypeName}[] {propInitFuncVarName}(JsonSerializerContext context)
- {{
- JsonContext {JsonContextVarName} = (JsonContext)context;
- JsonSerializerOptions options = context.Options;
-
- {JsonPropertyInfoTypeName}[] {PropVarName} = {propertyArrayInstantiationValue};
-");
-
- if (properties != null)
- {
- for (int i = 0; i < properties.Count; i++)
- {
- PropertyMetadata memberMetadata = properties[i];
-
- TypeMetadata memberTypeMetadata = memberMetadata.TypeMetadata;
-
- string clrPropertyName = memberMetadata.ClrName;
-
- string declaringTypeCompilableName = memberMetadata.DeclaringTypeCompilableName;
-
- string memberTypeFriendlyName = memberTypeMetadata.ClassType == ClassType.TypeUnsupportedBySourceGen
- ? "null"
- : $"{JsonContextVarName}.{memberTypeMetadata.FriendlyName}";
-
- string typeTypeInfoNamedArg = $"propertyTypeInfo: {memberTypeFriendlyName}";
-
- string jsonPropertyNameNamedArg = memberMetadata.JsonPropertyName != null
- ? @$"jsonPropertyName: ""{memberMetadata.JsonPropertyName}"""
- : "jsonPropertyName: null";
-
- string getterNamedArg = memberMetadata.HasGetter
- ? $"getter: static (obj) => {{ return (({declaringTypeCompilableName})obj).{clrPropertyName}; }}"
- : "getter: null";
-
- string setterNamedArg;
- if (memberMetadata.HasSetter)
- {
- string propMutation = declaringTypeIsValueType
- ? @$"{{ Unsafe.Unbox<{declaringTypeCompilableName}>(obj).{clrPropertyName} = value; }}"
- : $@"{{ (({declaringTypeCompilableName})obj).{clrPropertyName} = value; }}";
-
- setterNamedArg = $"setter: static (obj, value) => {propMutation}";
- }
- else
- {
- setterNamedArg = "setter: null";
- }
-
- JsonIgnoreCondition? ignoreCondition = memberMetadata.IgnoreCondition;
- string ignoreConditionNamedArg = ignoreCondition.HasValue
- ? $"ignoreCondition: JsonIgnoreCondition.{ignoreCondition.Value}"
- : "ignoreCondition: default";
-
- string converterNamedArg = memberMetadata.ConverterInstantiationLogic == null
- ? "converter: null"
- : $"converter: {memberMetadata.ConverterInstantiationLogic}";
-
- string memberTypeCompilableName = memberTypeMetadata.CompilableName;
-
- sb.Append($@"
- {PropVarName}[{i}] = {JsonMetadataServicesClassName}.CreatePropertyInfo<{memberTypeCompilableName}>(
- options,
- isProperty: {memberMetadata.IsProperty.ToString().ToLowerInvariant()},
- declaringType: typeof({memberMetadata.DeclaringTypeCompilableName}),
- {typeTypeInfoNamedArg},
- {converterNamedArg},
- {getterNamedArg},
- {setterNamedArg},
- {ignoreConditionNamedArg},
- numberHandling: {GetNumberHandlingAsStr(memberMetadata.NumberHandling)},
- propertyName: ""{clrPropertyName}"",
- {jsonPropertyNameNamedArg});
- ");
- }
- }
-
- sb.Append(@$"
- return {PropVarName};
- }}");
-
- return sb.ToString();
- }
-
- private string GenerateForType(TypeMetadata typeMetadata, string metadataInitSource, string? additionalSource = null)
- {
- string typeCompilableName = typeMetadata.CompilableName;
- string typeFriendlyName = typeMetadata.FriendlyName;
-
- return @$"{GetUsingStatementsString(typeMetadata)}
-
-namespace {_generationNamespace}
-{{
- {JsonContextDeclarationSource}
- {{
- private JsonTypeInfo<{typeCompilableName}> _{typeFriendlyName};
- public JsonTypeInfo<{typeCompilableName}> {typeFriendlyName}
- {{
- get
- {{
- if (_{typeFriendlyName} == null)
- {{
- {WrapWithCheckForCustomConverterIfRequired(metadataInitSource, typeCompilableName, typeFriendlyName, GetNumberHandlingAsStr(typeMetadata.NumberHandling))}
- }}
-
- return _{typeFriendlyName};
- }}
- }}{additionalSource}
- }}
-}}
-";
- }
-
- private string WrapWithCheckForCustomConverterIfRequired(string source, string typeCompilableName, string typeFriendlyName, string numberHandlingNamedArg)
- {
- if (!_honorRuntimeProvidedCustomConverters)
- {
- return source;
- }
-
- return @$"JsonConverter customConverter;
- if ({OptionsInstanceVariableName}.Converters.Count > 0 && (customConverter = {RuntimeCustomConverterFetchingMethodName}(typeof({typeCompilableName}))) != null)
- {{
- _{typeFriendlyName} = {JsonMetadataServicesClassName}.{GetCreateValueInfoMethodRef(typeCompilableName)}({OptionsInstanceVariableName}, customConverter);
- }}
- else
- {{
- {source.Replace(Environment.NewLine, $"{Environment.NewLine} ")}
- }}";
- }
-
- // Base source generation context partial class.
- private string BaseJsonContextImplementation()
- {
- StringBuilder sb = new();
- sb.Append(@$"using System.Text.Json;
-using System.Text.Json.Serialization;
-
-namespace {_generationNamespace}
-{{
- {JsonContextDeclarationSource}
- {{
- private static JsonContext s_default;
- public static JsonContext Default => s_default ??= new JsonContext(new JsonSerializerOptions());
-
- public JsonContext() : base(null)
- {{
- }}
-
- public JsonContext(JsonSerializerOptions options) : base(options)
- {{
- }}
-
- {GetFetchLogicForRuntimeSpecifiedCustomConverter()}
- }}
-}}
-");
-
- return sb.ToString();
- }
-
- private string GetFetchLogicForRuntimeSpecifiedCustomConverter()
- {
- if (!_honorRuntimeProvidedCustomConverters)
- {
- return "";
- }
-
- return @$"private JsonConverter {RuntimeCustomConverterFetchingMethodName}(System.Type type)
- {{
- System.Collections.Generic.IList converters = {OptionsInstanceVariableName}.Converters;
-
- // TODO: use a dictionary if count > ~15.
- for (int i = 0; i < converters.Count; i++)
- {{
- JsonConverter converter = converters[i];
-
- if (converter.CanConvert(type))
- {{
- if (converter is JsonConverterFactory factory)
- {{
- converter = factory.CreateConverter(type, {OptionsInstanceVariableName});
- if (converter == null || converter is JsonConverterFactory)
- {{
- throw new System.InvalidOperationException($""The converter '{{factory.GetType()}}' cannot return null or a JsonConverterFactory instance."");
- }}
- }}
-
- return converter;
- }}
- }}
-
- return null;
- }}";
- }
-
- private string GetGetTypeInfoImplementation()
- {
- StringBuilder sb = new();
-
- HashSet usingStatements = new();
-
- foreach (TypeMetadata metadata in _rootSerializableTypes.Values)
- {
- usingStatements.UnionWith(GetUsingStatements(metadata));
- }
-
- sb.Append(@$"{GetUsingStatementsString(usingStatements)}
-
-namespace {_generationNamespace}
-{{
- {JsonContextDeclarationSource}
- {{
- public override JsonTypeInfo GetTypeInfo(System.Type type)
- {{");
-
- // TODO: Make this Dictionary-lookup-based if _handledType.Count > 64.
- foreach (TypeMetadata metadata in _rootSerializableTypes.Values)
- {
- if (metadata.ClassType != ClassType.TypeUnsupportedBySourceGen)
- {
- sb.Append($@"
- if (type == typeof({metadata.Type.GetUniqueCompilableTypeName()}))
- {{
- return this.{metadata.FriendlyName};
- }}
-");
- }
- }
-
- sb.Append(@"
- return null!;
- }
- }
-}
-");
-
- return sb.ToString();
- }
-
- private static string GetUsingStatementsString(TypeMetadata typeMetadata)
- {
- HashSet usingStatements = GetUsingStatements(typeMetadata);
- return GetUsingStatementsString(usingStatements);
- }
-
- private static string GetUsingStatementsString(HashSet usingStatements)
- {
- string[] usingsArr = usingStatements.ToArray();
- Array.Sort(usingsArr);
- return string.Join("\n", usingsArr);
- }
-
- private static HashSet GetUsingStatements(TypeMetadata typeMetadata)
- {
- HashSet usingStatements = new();
-
- // Add library usings.
- usingStatements.Add(FormatAsUsingStatement("System.Runtime.CompilerServices"));
- usingStatements.Add(FormatAsUsingStatement("System.Text.Json"));
- usingStatements.Add(FormatAsUsingStatement("System.Text.Json.Serialization"));
- usingStatements.Add(FormatAsUsingStatement("System.Text.Json.Serialization.Metadata"));
-
- // Add imports to root type.
- usingStatements.Add(FormatAsUsingStatement(typeMetadata.Type.Namespace));
-
- switch (typeMetadata.ClassType)
- {
- case ClassType.Nullable:
- {
- AddUsingStatementsForType(typeMetadata.NullableUnderlyingTypeMetadata!);
- }
- break;
- case ClassType.Enumerable:
- {
- AddUsingStatementsForType(typeMetadata.CollectionValueTypeMetadata);
- }
- break;
- case ClassType.Dictionary:
- {
- AddUsingStatementsForType(typeMetadata.CollectionKeyTypeMetadata);
- AddUsingStatementsForType(typeMetadata.CollectionValueTypeMetadata);
- }
- break;
- case ClassType.Object:
- {
- if (typeMetadata.PropertiesMetadata != null)
- {
- foreach (PropertyMetadata metadata in typeMetadata.PropertiesMetadata)
- {
- AddUsingStatementsForType(metadata.TypeMetadata);
- }
- }
- }
- break;
- default:
- break;
- }
-
- void AddUsingStatementsForType(TypeMetadata typeMetadata)
- {
- usingStatements.Add(FormatAsUsingStatement(typeMetadata.Type.Namespace));
-
- if (typeMetadata.CollectionKeyTypeMetadata != null)
- {
- Debug.Assert(typeMetadata.CollectionValueTypeMetadata != null);
- usingStatements.Add(FormatAsUsingStatement(typeMetadata.CollectionKeyTypeMetadata.Type.Namespace));
- }
-
- if (typeMetadata.CollectionValueTypeMetadata != null)
- {
- usingStatements.Add(FormatAsUsingStatement(typeMetadata.CollectionValueTypeMetadata.Type.Namespace));
- }
- }
-
- return usingStatements;
- }
-
- private static string FormatAsUsingStatement(string @namespace) => $"using {@namespace};";
-
- private static string GetNumberHandlingAsStr(JsonNumberHandling? numberHandling) =>
- numberHandling.HasValue
- ? $"(JsonNumberHandling){(int)numberHandling.Value}"
- : "default";
-
- private static string GetCreateValueInfoMethodRef(string typeCompilableName) => $"{CreateValueInfoMethodName}<{typeCompilableName}>";
- }
-}
diff --git a/src/libraries/System.Text.Json/gen/JsonSourceGeneratorHelper.cs b/src/libraries/System.Text.Json/gen/JsonSourceGeneratorHelper.cs
deleted file mode 100644
index 10f1dd2f011a6..0000000000000
--- a/src/libraries/System.Text.Json/gen/JsonSourceGeneratorHelper.cs
+++ /dev/null
@@ -1,467 +0,0 @@
-// 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;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Reflection;
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.Text;
-using System.Text.Json.Serialization;
-using System.Text.Json.SourceGeneration.Reflection;
-using System.Linq;
-
-namespace System.Text.Json.SourceGeneration
-{
- internal sealed partial class JsonSourceGeneratorHelper
- {
- private readonly Type _ienumerableType;
- private readonly Type _listOfTType;
- private readonly Type _dictionaryType;
-
- private readonly Type _booleanType;
- private readonly Type _byteArrayType;
- private readonly Type _charType;
- private readonly Type _dateTimeType;
- private readonly Type _dateTimeOffsetType;
- private readonly Type _guidType;
- private readonly Type _stringType;
- private readonly Type _uriType;
- private readonly Type _versionType;
-
- private readonly HashSet _numberTypes = new();
-
- private readonly HashSet _knownTypes = new();
-
- ///
- /// Type information for member types in input object graphs.
- ///
- private readonly Dictionary _typeMetadataCache = new();
-
- ///
- /// Types that were specified with System.Text.Json.Serialization.JsonSerializableAttribute.
- ///
- private Dictionary _rootSerializableTypes;
-
- private readonly GeneratorExecutionContext _executionContext;
-
- private readonly MetadataLoadContextInternal _metadataLoadContext;
-
- private const string SystemTextJsonNamespace = "System.Text.Json";
-
- private const string JsonConverterAttributeFullName = "System.Text.Json.Serialization.JsonConverterAttribute";
-
- private const string JsonIgnoreAttributeFullName = "System.Text.Json.Serialization.JsonIgnoreAttribute";
-
- private const string JsonIgnoreConditionFullName = "System.Text.Json.Serialization.JsonIgnoreCondition";
-
- private const string JsonIncludeAttributeFullName = "System.Text.Json.Serialization.JsonIncludeAttribute";
-
- private const string JsonNumberHandlingAttributeFullName = "System.Text.Json.Serialization.JsonNumberHandlingAttribute";
-
- private const string JsonPropertyNameAttributeFullName = "System.Text.Json.Serialization.JsonPropertyNameAttribute";
-
- public JsonSourceGeneratorHelper(
- GeneratorExecutionContext executionContext,
- MetadataLoadContextInternal metadataLoadContext)
- {
- _generationNamespace = $"{executionContext.Compilation.AssemblyName}.JsonSourceGeneration";
- _executionContext = executionContext;
- _metadataLoadContext = metadataLoadContext;
-
- _ienumerableType = metadataLoadContext.Resolve(typeof(IEnumerable));
- _listOfTType = metadataLoadContext.Resolve(typeof(List<>));
- _dictionaryType = metadataLoadContext.Resolve(typeof(Dictionary<,>));
-
- _booleanType = metadataLoadContext.Resolve(typeof(bool));
- _byteArrayType = metadataLoadContext.Resolve(typeof(byte[]));
- _charType = metadataLoadContext.Resolve(typeof(char));
- _dateTimeType = metadataLoadContext.Resolve(typeof(DateTime));
- _dateTimeOffsetType = metadataLoadContext.Resolve(typeof(DateTimeOffset));
- _guidType = metadataLoadContext.Resolve(typeof(Guid));
- _stringType = metadataLoadContext.Resolve(typeof(string));
- _uriType = metadataLoadContext.Resolve(typeof(Uri));
- _versionType = metadataLoadContext.Resolve(typeof(Version));
-
- PopulateKnownTypes(metadataLoadContext);
- }
-
- public void RegisterRootSerializableType(Type type, string? typeInfoPropertyName)
- {
- _rootSerializableTypes ??= new Dictionary();
- _rootSerializableTypes[type.FullName] = GetOrAddTypeMetadata(type, typeInfoPropertyName);
- }
-
- public void GenerateSerializationMetadata()
- {
- if (_rootSerializableTypes == null || _rootSerializableTypes.Count == 0)
- {
- return;
- }
-
- foreach (KeyValuePair pair in _rootSerializableTypes)
- {
- TypeMetadata typeMetadata = pair.Value;
- GenerateTypeMetadata(typeMetadata);
- }
-
- // Add base default instance source.
- _executionContext.AddSource("JsonContext.g.cs", SourceText.From(BaseJsonContextImplementation(), Encoding.UTF8));
-
- // Add GetJsonTypeInfo override implementation.
- _executionContext.AddSource("JsonContext.GetJsonTypeInfo.g.cs", SourceText.From(GetGetTypeInfoImplementation(), Encoding.UTF8));
- }
-
- private TypeMetadata GetOrAddTypeMetadata(Type type, string? typeInfoPropertyName = null)
- {
- if (_typeMetadataCache.TryGetValue(type, out TypeMetadata? typeMetadata))
- {
- return typeMetadata!;
- }
-
- // Add metadata to cache now to prevent stack overflow when the same type is found somewhere else in the object graph.
- typeMetadata = new();
- _typeMetadataCache[type] = typeMetadata;
-
- ClassType classType;
- Type? collectionKeyType = null;
- Type? collectionValueType = null;
- Type? nullableUnderlyingType = null;
- List? propertiesMetadata = null;
- CollectionType collectionType = CollectionType.NotApplicable;
- ObjectConstructionStrategy constructionStrategy = default;
- JsonNumberHandling? numberHandling = null;
- bool containsOnlyPrimitives = true;
-
- bool foundDesignTimeCustomConverter = false;
- string? converterInstatiationLogic = null;
-
- IList attributeDataList = CustomAttributeData.GetCustomAttributes(type);
- foreach (CustomAttributeData attributeData in attributeDataList)
- {
- Type attributeType = attributeData.AttributeType;
- if (attributeType.FullName == "System.Text.Json.Serialization.JsonNumberHandlingAttribute")
- {
- IList ctorArgs = attributeData.ConstructorArguments;
- numberHandling = (JsonNumberHandling)ctorArgs[0].Value;
- continue;
- }
- else if (!foundDesignTimeCustomConverter && attributeType.GetCompatibleBaseClass(JsonConverterAttributeFullName) != null)
- {
- foundDesignTimeCustomConverter = true;
- converterInstatiationLogic = GetConverterInstantiationLogic(attributeData);
- }
- }
-
- if (foundDesignTimeCustomConverter)
- {
- classType = converterInstatiationLogic != null
- ? ClassType.TypeWithDesignTimeProvidedCustomConverter
- : ClassType.TypeUnsupportedBySourceGen;
- }
- else if (_knownTypes.Contains(type))
- {
- classType = ClassType.KnownType;
- }
- else if (type.IsNullableValueType(out nullableUnderlyingType))
- {
- Debug.Assert(nullableUnderlyingType != null);
- classType = ClassType.Nullable;
- }
- else if (type.IsEnum)
- {
- classType = ClassType.Enum;
- }
- else if (_ienumerableType.IsAssignableFrom(type))
- {
- // Only T[], List, and Dictionary are supported.
-
- if (type.IsArray)
- {
- classType = ClassType.Enumerable;
- collectionType = CollectionType.Array;
- collectionValueType = type.GetElementType();
- }
- else if (!type.IsGenericType)
- {
- classType = ClassType.TypeUnsupportedBySourceGen;
- }
- else
- {
- Type genericTypeDef = type.GetGenericTypeDefinition();
- Type[] genericTypeArgs = type.GetGenericArguments();
-
- if (genericTypeDef == _listOfTType)
- {
- classType = ClassType.Enumerable;
- collectionType = CollectionType.List;
- collectionValueType = genericTypeArgs[0];
- }
- else if (genericTypeDef == _dictionaryType)
- {
- classType = ClassType.Dictionary;
- collectionType = CollectionType.Dictionary;
- collectionKeyType = genericTypeArgs[0];
- collectionValueType = genericTypeArgs[1];
- }
- else
- {
- classType = ClassType.TypeUnsupportedBySourceGen;
- }
- }
- }
- else
- {
- classType = ClassType.Object;
-
- if (type.GetConstructor(Type.EmptyTypes) != null && !type.IsAbstract && !type.IsInterface)
- {
- // TODO: support parameterized ctors.
- constructionStrategy = ObjectConstructionStrategy.ParameterlessConstructor;
- }
-
- for (Type? currentType = type; currentType != null; currentType = currentType.BaseType)
- {
- const BindingFlags bindingFlags =
- BindingFlags.Instance |
- BindingFlags.Public |
- BindingFlags.NonPublic |
- BindingFlags.DeclaredOnly;
-
- foreach (PropertyInfo propertyInfo in currentType.GetProperties(bindingFlags))
- {
- PropertyMetadata metadata = GetPropertyMetadata(propertyInfo);
-
- // Ignore indexers.
- if (propertyInfo.GetIndexParameters().Length > 0)
- {
- continue;
- }
-
- string key = metadata.JsonPropertyName ?? metadata.ClrName;
-
- if (metadata.HasGetter || metadata.HasSetter)
- {
- (propertiesMetadata ??= new()).Add(metadata);
- }
-
- if (containsOnlyPrimitives && !IsPrimitive(propertyInfo.PropertyType))
- {
- containsOnlyPrimitives = false;
- }
- }
-
- foreach (FieldInfo fieldInfo in currentType.GetFields(bindingFlags))
- {
- PropertyMetadata metadata = GetPropertyMetadata(fieldInfo);
-
- if (metadata.HasGetter || metadata.HasSetter)
- {
- (propertiesMetadata ??= new()).Add(metadata);
- }
- }
- }
- }
-
- typeMetadata.Initialize(
- compilableName: type.GetUniqueCompilableTypeName(),
- friendlyName: typeInfoPropertyName ?? type.GetFriendlyTypeName(),
- type,
- classType,
- isValueType: type.IsValueType,
- numberHandling,
- propertiesMetadata,
- collectionType,
- collectionKeyTypeMetadata: collectionKeyType != null ? GetOrAddTypeMetadata(collectionKeyType) : null,
- collectionValueTypeMetadata: collectionValueType != null ? GetOrAddTypeMetadata(collectionValueType) : null,
- constructionStrategy,
- nullableUnderlyingTypeMetadata: nullableUnderlyingType != null ? GetOrAddTypeMetadata(nullableUnderlyingType) : null,
- converterInstatiationLogic,
- containsOnlyPrimitives);
-
- return typeMetadata;
- }
-
- private PropertyMetadata GetPropertyMetadata(MemberInfo memberInfo)
- {
- IList attributeDataList = CustomAttributeData.GetCustomAttributes(memberInfo);
-
- bool hasJsonInclude = false;
- JsonIgnoreCondition? ignoreCondition = null;
- JsonNumberHandling? numberHandling = null;
- string? jsonPropertyName = null;
-
- bool foundDesignTimeCustomConverter = false;
- string? converterInstantiationLogic = null;
-
- foreach (CustomAttributeData attributeData in attributeDataList)
- {
- Type attributeType = attributeData.AttributeType;
-
- if (!foundDesignTimeCustomConverter && attributeType.GetCompatibleBaseClass(JsonConverterAttributeFullName) != null)
- {
- foundDesignTimeCustomConverter = true;
- converterInstantiationLogic = GetConverterInstantiationLogic(attributeData);
- }
- else if (attributeType.Assembly.FullName == SystemTextJsonNamespace)
- {
- switch (attributeData.AttributeType.FullName)
- {
- case JsonIgnoreAttributeFullName:
- {
- IList namedArgs = attributeData.NamedArguments;
-
- if (namedArgs.Count == 0)
- {
- ignoreCondition = JsonIgnoreCondition.Always;
- }
- else if (namedArgs.Count == 1 &&
- namedArgs[0].MemberInfo.MemberType == MemberTypes.Property &&
- ((PropertyInfo)namedArgs[0].MemberInfo).PropertyType.FullName == JsonIgnoreConditionFullName)
- {
- ignoreCondition = (JsonIgnoreCondition)namedArgs[0].TypedValue.Value;
- }
- }
- break;
- case JsonIncludeAttributeFullName:
- {
- hasJsonInclude = true;
- }
- break;
- case JsonNumberHandlingAttributeFullName:
- {
- IList ctorArgs = attributeData.ConstructorArguments;
- numberHandling = (JsonNumberHandling)ctorArgs[0].Value;
- }
- break;
- case JsonPropertyNameAttributeFullName:
- {
- IList ctorArgs = attributeData.ConstructorArguments;
- jsonPropertyName = (string)ctorArgs[0].Value;
- // Null check here is done at runtime within JsonSerializer.
- }
- break;
- default:
- break;
- }
- }
- }
-
- Type memberCLRType;
- bool hasGetter;
- bool hasSetter;
- bool getterIsVirtual = false;
- bool setterIsVirtual = false;
-
- switch (memberInfo)
- {
- case PropertyInfo propertyInfo:
- {
- MethodInfo setMethod = propertyInfo.SetMethod;
-
- memberCLRType = propertyInfo.PropertyType;
- hasGetter = PropertyAccessorCanBeReferenced(propertyInfo.GetMethod, hasJsonInclude);
- hasSetter = PropertyAccessorCanBeReferenced(setMethod, hasJsonInclude) && !setMethod.IsInitOnly();
- getterIsVirtual = propertyInfo.GetMethod?.IsVirtual == true;
- setterIsVirtual = propertyInfo.SetMethod?.IsVirtual == true;
- }
- break;
- case FieldInfo fieldInfo:
- {
- Debug.Assert(fieldInfo.IsPublic);
-
- memberCLRType = fieldInfo.FieldType;
- hasGetter = true;
- hasSetter = !fieldInfo.IsInitOnly;
- }
- break;
- default:
- throw new InvalidOperationException();
- }
-
- return new PropertyMetadata
- {
- ClrName = memberInfo.Name,
- IsProperty = memberInfo.MemberType == MemberTypes.Property,
- JsonPropertyName = jsonPropertyName,
- HasGetter = hasGetter,
- HasSetter = hasSetter,
- GetterIsVirtual = getterIsVirtual,
- SetterIsVirtual = setterIsVirtual,
- IgnoreCondition = ignoreCondition,
- NumberHandling = numberHandling,
- HasJsonInclude = hasJsonInclude,
- TypeMetadata = GetOrAddTypeMetadata(memberCLRType),
- DeclaringTypeCompilableName = memberInfo.DeclaringType.GetUniqueCompilableTypeName(),
- ConverterInstantiationLogic = converterInstantiationLogic
- };
- }
-
- private static bool PropertyAccessorCanBeReferenced(MethodInfo? memberAccessor, bool hasJsonInclude) =>
- (memberAccessor != null && !memberAccessor.IsPrivate) && (memberAccessor.IsPublic || hasJsonInclude);
-
- private string? GetConverterInstantiationLogic(CustomAttributeData attributeData)
- {
- if (attributeData.AttributeType.FullName != JsonConverterAttributeFullName)
- {
- return null;
- }
-
- Type converterType = new TypeWrapper((ITypeSymbol)attributeData.ConstructorArguments[0].Value, _metadataLoadContext);
-
- if (converterType == null || converterType.GetConstructor(Type.EmptyTypes) == null || converterType.IsNestedPrivate)
- {
- return null;
- }
-
- return $"new {converterType.GetUniqueCompilableTypeName()}()";
- }
-
- private void PopulateNumberTypes(MetadataLoadContextInternal metadataLoadContext)
- {
- Debug.Assert(_numberTypes != null);
- _numberTypes.Add(metadataLoadContext.Resolve(typeof(byte)));
- _numberTypes.Add(metadataLoadContext.Resolve(typeof(decimal)));
- _numberTypes.Add(metadataLoadContext.Resolve(typeof(double)));
- _numberTypes.Add(metadataLoadContext.Resolve(typeof(short)));
- _numberTypes.Add(metadataLoadContext.Resolve(typeof(sbyte)));
- _numberTypes.Add(metadataLoadContext.Resolve(typeof(int)));
- _numberTypes.Add(metadataLoadContext.Resolve(typeof(long)));
- _numberTypes.Add(metadataLoadContext.Resolve(typeof(float)));
- _numberTypes.Add(metadataLoadContext.Resolve(typeof(ushort)));
- _numberTypes.Add(metadataLoadContext.Resolve(typeof(uint)));
- _numberTypes.Add(metadataLoadContext.Resolve(typeof(ulong)));
- }
-
- private void PopulateKnownTypes(MetadataLoadContextInternal metadataLoadContext)
- {
- PopulateNumberTypes(metadataLoadContext);
-
- Debug.Assert(_knownTypes != null);
- Debug.Assert(_numberTypes != null);
-
- _knownTypes.UnionWith(_numberTypes);
- _knownTypes.Add(_booleanType);
- _knownTypes.Add(_byteArrayType);
- _knownTypes.Add(_charType);
- _knownTypes.Add(_dateTimeType);
- _knownTypes.Add(_dateTimeOffsetType);
- _knownTypes.Add(_guidType);
- _knownTypes.Add(metadataLoadContext.Resolve(typeof(object)));
- _knownTypes.Add(_stringType);
-
- // System.Private.Uri may not be loaded in input compilation.
- if (_uriType != null)
- {
- _knownTypes.Add(_uriType);
- }
-
- _knownTypes.Add(metadataLoadContext.Resolve(typeof(Version)));
- }
-
- private bool IsPrimitive(Type type)
- => _knownTypes.Contains(type) && type != _uriType && type != _versionType;
-
- public Dictionary? GetSerializableTypes() => _rootSerializableTypes?.ToDictionary(p => p.Key, p => p.Value.Type);
- }
-}
diff --git a/src/libraries/System.Text.Json/gen/Reflection/ReflectionExtensions.cs b/src/libraries/System.Text.Json/gen/Reflection/ReflectionExtensions.cs
index 07dce68fcbb89..1c0619ddda58e 100644
--- a/src/libraries/System.Text.Json/gen/Reflection/ReflectionExtensions.cs
+++ b/src/libraries/System.Text.Json/gen/Reflection/ReflectionExtensions.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Diagnostics;
using System.Linq;
using System.Reflection;
@@ -20,13 +21,12 @@ public static TValue GetConstructorArgument(this CustomAttributeData cus
public static bool IsInitOnly(this MethodInfo method)
{
- MethodInfoWrapper? methodInfoWrapper = method as MethodInfoWrapper;
-
- if (methodInfoWrapper == null)
+ if (method == null)
{
- throw new ArgumentException("Expected a MethodInfoWrapper instance.", nameof(method));
+ throw new ArgumentNullException(nameof(method));
}
+ MethodInfoWrapper methodInfoWrapper = (MethodInfoWrapper)method;
return methodInfoWrapper.IsInitOnly;
}
}
diff --git a/src/libraries/System.Text.Json/gen/Resources/Strings.resx b/src/libraries/System.Text.Json/gen/Resources/Strings.resx
new file mode 100644
index 0000000000000..4560355948055
--- /dev/null
+++ b/src/libraries/System.Text.Json/gen/Resources/Strings.resx
@@ -0,0 +1,132 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision.
+
+
+ Duplicate type name.
+
+
+ Did not generate serialization metadata for type '{0}'.
+
+
+ Did not generate serialization metadata for type.
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf
new file mode 100644
index 0000000000000..85182d5d5047a
--- /dev/null
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision.
+
+
+
+
+ Duplicate type name.
+
+
+
+
+ Did not generate serialization metadata for type '{0}'.
+
+
+
+
+ Did not generate serialization metadata for type.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf
new file mode 100644
index 0000000000000..1b94670b3e543
--- /dev/null
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision.
+
+
+
+
+ Duplicate type name.
+
+
+
+
+ Did not generate serialization metadata for type '{0}'.
+
+
+
+
+ Did not generate serialization metadata for type.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf
new file mode 100644
index 0000000000000..55915ce81424e
--- /dev/null
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision.
+
+
+
+
+ Duplicate type name.
+
+
+
+
+ Did not generate serialization metadata for type '{0}'.
+
+
+
+
+ Did not generate serialization metadata for type.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf
new file mode 100644
index 0000000000000..cb63078ef065e
--- /dev/null
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision.
+
+
+
+
+ Duplicate type name.
+
+
+
+
+ Did not generate serialization metadata for type '{0}'.
+
+
+
+
+ Did not generate serialization metadata for type.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf
new file mode 100644
index 0000000000000..2ac4e6fc16613
--- /dev/null
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision.
+
+
+
+
+ Duplicate type name.
+
+
+
+
+ Did not generate serialization metadata for type '{0}'.
+
+
+
+
+ Did not generate serialization metadata for type.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf
new file mode 100644
index 0000000000000..4661920d08a5c
--- /dev/null
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision.
+
+
+
+
+ Duplicate type name.
+
+
+
+
+ Did not generate serialization metadata for type '{0}'.
+
+
+
+
+ Did not generate serialization metadata for type.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf
new file mode 100644
index 0000000000000..322cc79f11a3b
--- /dev/null
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision.
+
+
+
+
+ Duplicate type name.
+
+
+
+
+ Did not generate serialization metadata for type '{0}'.
+
+
+
+
+ Did not generate serialization metadata for type.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf
new file mode 100644
index 0000000000000..408e27c293575
--- /dev/null
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision.
+
+
+
+
+ Duplicate type name.
+
+
+
+
+ Did not generate serialization metadata for type '{0}'.
+
+
+
+
+ Did not generate serialization metadata for type.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf
new file mode 100644
index 0000000000000..e131d2555c888
--- /dev/null
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision.
+
+
+
+
+ Duplicate type name.
+
+
+
+
+ Did not generate serialization metadata for type '{0}'.
+
+
+
+
+ Did not generate serialization metadata for type.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf
new file mode 100644
index 0000000000000..f02ca1c7c22a7
--- /dev/null
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision.
+
+
+
+
+ Duplicate type name.
+
+
+
+
+ Did not generate serialization metadata for type '{0}'.
+
+
+
+
+ Did not generate serialization metadata for type.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf
new file mode 100644
index 0000000000000..2a00a92caf8e6
--- /dev/null
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision.
+
+
+
+
+ Duplicate type name.
+
+
+
+
+ Did not generate serialization metadata for type '{0}'.
+
+
+
+
+ Did not generate serialization metadata for type.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf
new file mode 100644
index 0000000000000..2822193d1924a
--- /dev/null
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision.
+
+
+
+
+ Duplicate type name.
+
+
+
+
+ Did not generate serialization metadata for type '{0}'.
+
+
+
+
+ Did not generate serialization metadata for type.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf
new file mode 100644
index 0000000000000..c5c1cc188a7f8
--- /dev/null
+++ b/src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ There are multiple types named {0}. Source was generated for the first one detected. Use 'JsonSerializableAttribute.TypeInfoPropertyName' to resolve this collision.
+
+
+
+
+ Duplicate type name.
+
+
+
+
+ Did not generate serialization metadata for type '{0}'.
+
+
+
+
+ Did not generate serialization metadata for type.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj
index 92e70583da672..74dd28a5b00a3 100644
--- a/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj
+++ b/src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.csproj
@@ -5,6 +5,8 @@
enable
CS1574
+ false
+ true
@@ -19,15 +21,14 @@
+
-
-
-
-
+
+
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.UnitTests/JsonSourceGeneratorTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.UnitTests/JsonSourceGeneratorTests.cs
index 945c79f044cff..f462ca0dd14ef 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.UnitTests/JsonSourceGeneratorTests.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.UnitTests/JsonSourceGeneratorTests.cs
@@ -56,16 +56,18 @@ public void UsePrivates()
CheckCompilationDiagnosticsErrors(generatorDiags);
CheckCompilationDiagnosticsErrors(newCompilation.GetDiagnostics());
+ Dictionary types = generator.GetSerializableTypes();
+
// Check base functionality of found types.
- Assert.Equal(1, generator.SerializableTypes.Count);
- Type myType= generator.SerializableTypes["HelloWorld.MyType"];
+ Assert.Equal(1, types.Count);
+ Type myType = types["HelloWorld.MyType"];
Assert.Equal("HelloWorld.MyType", myType.FullName);
// Check for received fields, properties and methods in created type.
string[] expectedPropertyNames = { "PublicPropertyInt", "PublicPropertyString",};
string[] expectedFieldNames = { "PublicChar", "PublicDouble" };
string[] expectedMethodNames = { "get_PrivatePropertyInt", "get_PrivatePropertyString", "get_PublicPropertyInt", "get_PublicPropertyString", "MyMethod", "MySecondMethod", "set_PrivatePropertyInt", "set_PrivatePropertyString", "set_PublicPropertyInt", "set_PublicPropertyString", "UsePrivates" };
- CheckFieldsPropertiesMethods("HelloWorld.MyType", ref generator, expectedFieldNames, expectedPropertyNames, expectedMethodNames);
+ CheckFieldsPropertiesMethods(myType, expectedFieldNames, expectedPropertyNames, expectedMethodNames);
}
[Fact]
@@ -122,10 +124,12 @@ public void UsePrivates()
CheckCompilationDiagnosticsErrors(generatorDiags);
CheckCompilationDiagnosticsErrors(newCompilation.GetDiagnostics());
+ Dictionary types = generator.GetSerializableTypes();
+
// Check base functionality of found types.
- Assert.Equal(2, generator.SerializableTypes.Count);
- Type myType = generator.SerializableTypes["HelloWorld.MyType"];
- Type notMyType = generator.SerializableTypes["ReferencedAssembly.Location"];
+ Assert.Equal(2, types.Count);
+ Type myType = types["HelloWorld.MyType"];
+ Type notMyType = types["ReferencedAssembly.Location"];
// Check for MyType.
Assert.Equal("HelloWorld.MyType", myType.FullName);
@@ -134,7 +138,7 @@ public void UsePrivates()
string[] expectedFieldNamesMyType = { "PublicChar", "PublicDouble" };
string[] expectedPropertyNamesMyType = { "PublicPropertyInt", "PublicPropertyString" };
string[] expectedMethodNamesMyType = { "get_PrivatePropertyInt", "get_PrivatePropertyString", "get_PublicPropertyInt", "get_PublicPropertyString", "MyMethod", "MySecondMethod", "set_PrivatePropertyInt", "set_PrivatePropertyString", "set_PublicPropertyInt", "set_PublicPropertyString", "UsePrivates" };
- CheckFieldsPropertiesMethods("HelloWorld.MyType", ref generator, expectedFieldNamesMyType, expectedPropertyNamesMyType, expectedMethodNamesMyType);
+ CheckFieldsPropertiesMethods(myType, expectedFieldNamesMyType, expectedPropertyNamesMyType, expectedMethodNamesMyType);
// Check for NotMyType.
Assert.Equal("ReferencedAssembly.Location", notMyType.FullName);
@@ -144,7 +148,7 @@ public void UsePrivates()
string[] expectedPropertyNamesNotMyType = { "Address1", "Address2", "City", "Country", "Id", "Name", "PhoneNumber", "PostalCode", "State" };
string[] expectedMethodNamesNotMyType = { "get_Address1", "get_Address2", "get_City", "get_Country", "get_Id", "get_Name", "get_PhoneNumber", "get_PostalCode", "get_State",
"set_Address1", "set_Address2", "set_City", "set_Country", "set_Id", "set_Name", "set_PhoneNumber", "set_PostalCode", "set_State" };
- CheckFieldsPropertiesMethods("ReferencedAssembly.Location", ref generator, expectedFieldNamesNotMyType, expectedPropertyNamesNotMyType, expectedMethodNamesNotMyType);
+ CheckFieldsPropertiesMethods(notMyType, expectedFieldNamesNotMyType, expectedPropertyNamesNotMyType, expectedMethodNamesNotMyType);
}
[Fact]
@@ -204,27 +208,31 @@ public void UsePrivates()
CheckCompilationDiagnosticsErrors(generatorDiags);
CheckCompilationDiagnosticsErrors(newCompilation.GetDiagnostics());
+ Dictionary types = generator.GetSerializableTypes();
+
// Check base functionality of found types.
- Assert.Equal(2, generator.SerializableTypes.Count);
+ Assert.Equal(2, types.Count);
// Check for MyType.
- Assert.Equal("HelloWorld.MyType", generator.SerializableTypes["HelloWorld.MyType"].FullName);
+ Type myType = types["HelloWorld.MyType"];
+ Assert.Equal("HelloWorld.MyType", myType.FullName);
// Check for received fields, properties and methods for MyType.
string[] expectedFieldNamesMyType = { "PublicChar", "PublicDouble" };
string[] expectedPropertyNamesMyType = { "PublicPropertyInt", "PublicPropertyString" };
string[] expectedMethodNamesMyType = { "get_PrivatePropertyInt", "get_PrivatePropertyString", "get_PublicPropertyInt", "get_PublicPropertyString", "MyMethod", "MySecondMethod", "set_PrivatePropertyInt", "set_PrivatePropertyString", "set_PublicPropertyInt", "set_PublicPropertyString", "UsePrivates" };
- CheckFieldsPropertiesMethods("HelloWorld.MyType", ref generator, expectedFieldNamesMyType, expectedPropertyNamesMyType, expectedMethodNamesMyType);
+ CheckFieldsPropertiesMethods(myType, expectedFieldNamesMyType, expectedPropertyNamesMyType, expectedMethodNamesMyType);
// Check for NotMyType.
- Assert.Equal("ReferencedAssembly.Location", generator.SerializableTypes["ReferencedAssembly.Location"].FullName);
+ Type notMyType = types["ReferencedAssembly.Location"];
+ Assert.Equal("ReferencedAssembly.Location", notMyType.FullName);
// Check for received fields, properties and methods for NotMyType.
string[] expectedFieldNamesNotMyType = { };
string[] expectedPropertyNamesNotMyType = { "Address1", "Address2", "City", "Country", "Id", "Name", "PhoneNumber", "PostalCode", "State" };
string[] expectedMethodNamesNotMyType = { "get_Address1", "get_Address2", "get_City", "get_Country", "get_Id", "get_Name", "get_PhoneNumber", "get_PostalCode", "get_State",
"set_Address1", "set_Address2", "set_City", "set_Country", "set_Id", "set_Name", "set_PhoneNumber", "set_PostalCode", "set_State" };
- CheckFieldsPropertiesMethods("ReferencedAssembly.Location", ref generator, expectedFieldNamesNotMyType, expectedPropertyNamesNotMyType, expectedMethodNamesNotMyType );
+ CheckFieldsPropertiesMethods(notMyType, expectedFieldNamesNotMyType, expectedPropertyNamesNotMyType, expectedMethodNamesNotMyType );
}
[Theory]
@@ -256,7 +264,7 @@ public JsonSerializableAttribute(Type type) { }
CompilationHelper.RunGenerators(compilation, out ImmutableArray generatorDiags, generator);
- Dictionary types = generator.SerializableTypes;
+ Dictionary types = generator.GetSerializableTypes();
if (includeSTJ)
{
Assert.Equal("System.Int32", types["System.Int32"].FullName);
@@ -297,7 +305,7 @@ public JsonSerializableAttribute(string typeInfoPropertyName, Type type) { }
JsonSourceGenerator generator = new JsonSourceGenerator();
CompilationHelper.RunGenerators(compilation, out ImmutableArray generatorDiags, generator);
- Assert.Null(generator.SerializableTypes);
+ Assert.Null(generator.GetSerializableTypes());
CompilationHelper.CheckDiagnosticMessages(generatorDiags, DiagnosticSeverity.Info, Array.Empty());
CompilationHelper.CheckDiagnosticMessages(generatorDiags, DiagnosticSeverity.Warning, Array.Empty());
@@ -369,9 +377,8 @@ private void CheckCompilationDiagnosticsErrors(ImmutableArray diagno
Assert.Empty(diagnostics.Where(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error));
}
- private void CheckFieldsPropertiesMethods(string typeName, ref JsonSourceGenerator generator, string[] expectedFields, string[] expectedProperties, string[] expectedMethods)
+ private void CheckFieldsPropertiesMethods(Type type, string[] expectedFields, string[] expectedProperties, string[] expectedMethods)
{
- Type type = generator.SerializableTypes[typeName];
string[] receivedFields = type.GetFields().Select(field => field.Name).OrderBy(s => s).ToArray();
string[] receivedProperties = type.GetProperties().Select(property => property.Name).OrderBy(s => s).ToArray();
string[] receivedMethods = type.GetMethods().Select(method => method.Name).OrderBy(s => s).ToArray();
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.UnitTests/TypeWrapperTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.UnitTests/TypeWrapperTests.cs
index 6f16665ced954..e08ee5ecaa2e0 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.UnitTests/TypeWrapperTests.cs
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.UnitTests/TypeWrapperTests.cs
@@ -1,6 +1,7 @@
// 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.Collections.Immutable;
using System.IO;
using System.Linq;
@@ -71,7 +72,7 @@ public void MySecondMethod() { }
Assert.Empty(newCompilation.GetDiagnostics().Where(diag => diag.Severity.Equals(DiagnosticSeverity.Error)));
// Should find both types since compilation above was successful.
- Assert.Equal(2, generator.SerializableTypes.Count);
+ Assert.Equal(2, generator.GetSerializableTypes().Count);
}
[Fact]
@@ -128,8 +129,9 @@ public void MySecondMethod() { }
Compilation outCompilation = CompilationHelper.RunGenerators(compilation, out ImmutableArray generatorDiags, generator);
// Check base functionality of found types.
- Assert.Equal(1, generator.SerializableTypes.Count);
- Type foundType = generator.SerializableTypes.First().Value;
+ Dictionary types = generator.GetSerializableTypes();
+ Assert.Equal(1, types.Count);
+ Type foundType = types.First().Value;
Assert.Equal("HelloWorld.MyType", foundType.FullName);
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/IsExternalInit.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/IsExternalInit.cs
deleted file mode 100644
index d789ecb6e2a18..0000000000000
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/IsExternalInit.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-// 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
-{
- ///
- /// Dummy class so C# 'Record' types can compile on NetStandard and NetFx.
- ///
- public sealed class IsExternalInit { }
-}
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj
index fa45a2859cc5c..62488ff19037e 100644
--- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj
+++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj
@@ -189,7 +189,7 @@
-
+