| File: Model\TypeGenerationSpec.cs | Web Access |
| Project: src\runtime\src\libraries\System.Text.Json\gen\System.Text.Json.SourceGeneration.Roslyn4.4.csproj (System.Text.Json.SourceGeneration) |
// 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.Text.Json.Serialization; using Microsoft.CodeAnalysis; using SourceGenerators; namespace System.Text.Json.SourceGeneration { /// <summary> /// Models a generated type. /// </summary> /// <remarks> /// Type needs to be cacheable as a Roslyn incremental value so it must be /// /// 1) immutable and /// 2) implement structural (pointwise) equality comparison. /// /// We can get these properties for free provided that we /// /// a) define the type as an immutable C# record and /// b) ensure all nested members are also immutable and implement structural equality. /// /// When adding new members to the type, please ensure that these properties /// are satisfied otherwise we risk breaking incremental caching in the source generator! /// </remarks> [DebuggerDisplay("Type = {TypeRef.Name}, ClassType = {ClassType}")] public sealed record TypeGenerationSpec { /// <summary> /// The type being generated. /// </summary> public required TypeRef TypeRef { get; init; } /// <summary> /// The name of the public <c>JsonTypeInfo<T></c> property for this type on the generated context class. /// For example, if the context class is named MyJsonContext, and the value of this property is JsonMessage; /// then users will call MyJsonContext.JsonMessage to access generated metadata for the type. /// </summary> public required string TypeInfoPropertyName { get; init; } public required JsonSourceGenerationMode GenerationMode { get; init; } public required JsonPrimitiveTypeKind? PrimitiveTypeKind { get; init; } public required ClassType ClassType { get; init; } public required bool ImplementsIJsonOnSerialized { get; init; } public required bool ImplementsIJsonOnSerializing { get; init; } public required bool IsPolymorphic { get; init; } public required PolymorphismOptionsSpec? PolymorphismOptions { get; init; } /// <summary> /// Indicates a closed type whose polymorphism metadata was not inferred at compile time /// (because <see cref="SourceGenerationOptionsSpec.InferClosedTypePolymorphism"/> was not enabled). /// Used to emit a marker so the runtime can fail fast if closed-type inference is requested there. /// </summary> public required bool IsClosedTypeWithoutInferredPolymorphism { get; init; } public required bool IsValueTuple { get; init; } public required JsonNumberHandling? NumberHandling { get; init; } public required JsonUnmappedMemberHandling? UnmappedMemberHandling { get; init; } public required JsonObjectCreationHandling? PreferredPropertyObjectCreationHandling { get; init; } /// <summary> /// List of all properties without conflict resolution or sorting to be generated for the metadata-based serializer. /// </summary> public required ImmutableEquatableArray<PropertyGenerationSpec> PropertyGenSpecs { get; init; } /// <summary> /// List of properties with compile-time conflict resolution and sorting to be generated for the fast-path serializer. /// Contains indices pointing to <see cref="PropertyGenSpecs"/>. A <see cref="null"/> value in the case of object types /// indicates that a naming conflict was found and that an exception throwing stub should be emitted in the fast-path method. /// </summary> public required ImmutableEquatableArray<int>? FastPathPropertyIndices { get; init; } /// <summary> /// List of case specs for compiler union metadata generation. /// </summary> public required ImmutableEquatableArray<UnionCaseSpec> UnionCaseSpecs { get; init; } /// <summary> /// Per-type classifier factory specified via <see cref="JsonUnionAttribute.TypeClassifier"/> annotations. /// </summary> public required TypeRef? UnionClassifierFactoryType { get; init; } public required ImmutableEquatableArray<ParameterGenerationSpec> CtorParamGenSpecs { get; init; } public required ImmutableEquatableArray<PropertyInitializerGenerationSpec> PropertyInitializerSpecs { get; init; } public required CollectionType CollectionType { get; init; } public required TypeRef? CollectionKeyType { get; init; } public required TypeRef? CollectionValueType { get; init; } public required ObjectConstructionStrategy ConstructionStrategy { get; init; } public required bool ConstructorSetsRequiredParameters { get; init; } /// <summary> /// Whether the deserialization constructor is inaccessible from the generated context. /// When true, UnsafeAccessor or reflection is used to invoke the constructor. /// </summary> public required bool ConstructorIsInaccessible { get; init; } /// <summary> /// Whether UnsafeAccessors can be used for the constructor. /// This is true for non-generic types on .NET 8+ and for generic types on .NET 9+ /// (using a generic wrapper class). False when <c>UnsafeAccessorAttribute</c> is not available. /// </summary> public required bool CanUseUnsafeAccessorForConstructor { get; init; } public required TypeRef? NullableUnderlyingType { get; init; } /// <summary> /// Supports deserialization of extension data dictionaries typed as <c>I[ReadOnly]Dictionary<string, object/JsonElement></c>. /// Specifies a concrete type to instantiate, which would be <c>Dictionary<string, object/JsonElement></c>. /// </summary> public required TypeRef? RuntimeTypeRef { get; init; } public required bool HasExtensionDataPropertyType { get; init; } public required TypeRef? ConverterType { get; init; } public required string? ImmutableCollectionFactoryMethod { get; init; } /// <summary> /// The distinct set of <c>[Experimental]</c> diagnostic IDs referenced by this type's generated code /// (the type itself, its serialized members and their types, its constructor and parameters, /// its polymorphic derived types, and its converters). Sorted for stable incremental-cache equality. /// The generated file for this type suppresses each of these IDs. /// </summary> public required ImmutableEquatableArray<string> ExperimentalDiagnosticIds { get; init; } public bool IsFastPathSupported() { if (ClassType is ClassType.Union) { return false; } if (IsPolymorphic) { return false; } if (JsonHelpers.RequiresSpecialNumberHandlingOnWrite(NumberHandling)) { return false; } switch (ClassType) { case ClassType.Object: if (HasExtensionDataPropertyType) { return false; } foreach (PropertyGenerationSpec property in PropertyGenSpecs) { if (property.PropertyType.SpecialType is SpecialType.System_Object || property.NumberHandling != null || property.ConverterType != null) { return false; } } return true; case ClassType.Enumerable: return CollectionType != CollectionType.IAsyncEnumerableOfT && CollectionValueType!.SpecialType is not SpecialType.System_Object; case ClassType.Dictionary: return CollectionKeyType!.SpecialType is SpecialType.System_String && CollectionValueType!.SpecialType is not SpecialType.System_Object; default: return false; } } } }