File: System\Text\Json\Serialization\WriteStackFrame.cs
Web Access
Project: src\src\libraries\System.Text.Json\src\System.Text.Json.csproj (System.Text.Json)
// 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.Diagnostics;
using System.Runtime.InteropServices;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
 
namespace System.Text.Json
{
    [StructLayout(LayoutKind.Auto)]
    [DebuggerDisplay("{DebuggerDisplay,nq}")]
    internal struct WriteStackFrame
    {
        /// <summary>
        /// The enumerator for resumable collections.
        /// </summary>
        public IEnumerator? CollectionEnumerator;
 
        /// <summary>
        /// The enumerator for resumable async disposables.
        /// </summary>
        public IAsyncDisposable? AsyncDisposable;
 
        /// <summary>
        /// The current stackframe has suspended serialization due to a pending task,
        /// stored in the <see cref="WriteStack.PendingTask"/> property.
        /// </summary>
        public bool AsyncEnumeratorIsPendingCompletion;
 
        /// <summary>
        /// The original JsonPropertyInfo that is not changed. It contains all properties.
        /// </summary>
        /// <remarks>
        /// For objects, it is either the actual (real) JsonPropertyInfo or the <see cref="JsonTypeInfo.PropertyInfoForTypeInfo"/> for the class.
        /// For collections, it is the <see cref="JsonTypeInfo.PropertyInfoForTypeInfo"/> for the class and current element.
        /// </remarks>
        public JsonPropertyInfo? JsonPropertyInfo;
 
        /// <summary>
        /// Used when processing extension data dictionaries.
        /// </summary>
        public bool IsWritingExtensionDataProperty;
 
        /// <summary>
        /// The class (POCO or IEnumerable) that is being populated.
        /// </summary>
        public JsonTypeInfo JsonTypeInfo;
 
        /// <summary>
        /// Validation state for a class.
        /// </summary>
        public int OriginalDepth;
 
        // Class-level state for collections.
        public bool ProcessedStartToken;
        public bool ProcessedEndToken;
 
        /// <summary>
        /// Property or Element state.
        /// </summary>
        public StackFramePropertyState PropertyState;
 
        /// <summary>
        /// The enumerator index for resumable collections.
        /// </summary>
        public int EnumeratorIndex;
 
        // This is used for re-entry cases for exception handling.
        public string? JsonPropertyNameAsString;
 
        // Preserve Reference
        public MetadataPropertyName MetadataPropertyName;
 
        // Serialization state for the child value serialized by the current frame.
        public PolymorphicSerializationState PolymorphicSerializationState;
        // Holds the entered polymorphic type info and acts as an LRU cache for element/field serializations.
        public JsonTypeInfo? PolymorphicTypeInfo;
 
        // Whether to use custom number handling.
        public JsonNumberHandling? NumberHandling;
 
        public bool IsPushedReferenceForCycleDetection;
 
        public void EndCollectionElement()
        {
            PolymorphicSerializationState = PolymorphicSerializationState.None;
        }
 
        public void EndDictionaryEntry()
        {
            PropertyState = StackFramePropertyState.None;
            PolymorphicSerializationState = PolymorphicSerializationState.None;
        }
 
        public void EndProperty()
        {
            JsonPropertyInfo = null!;
            JsonPropertyNameAsString = null;
            PropertyState = StackFramePropertyState.None;
            PolymorphicSerializationState = PolymorphicSerializationState.None;
        }
 
        /// <summary>
        /// Returns the JsonTypeInfo instance for the nested value we are trying to access.
        /// </summary>
        public readonly JsonTypeInfo GetNestedJsonTypeInfo()
        {
            return PolymorphicSerializationState is PolymorphicSerializationState.PolymorphicReEntryStarted
                ? PolymorphicTypeInfo!
                : JsonPropertyInfo!.JsonTypeInfo;
        }
 
        /// <summary>
        /// Configures the next stack frame for a polymorphic converter.
        /// </summary>
        public JsonTypeInfo InitializePolymorphicReEntry(Type runtimeType, JsonSerializerOptions options)
        {
            Debug.Assert(PolymorphicSerializationState == PolymorphicSerializationState.None);
 
            // For perf, avoid the dictionary lookup in GetTypeInfoInternal() for every element of a collection
            // if the current element is the same type as the previous element.
            if (PolymorphicTypeInfo?.Type != runtimeType)
            {
                // To determine the contract for an object value:
                // 1. Find the JsonTypeInfo for the runtime type with fallback to the nearest ancestor, if not available.
                // 2. If the resolved type is deriving from a polymorphic type, use the contract of the polymorphic type instead.
                JsonTypeInfo typeInfo = options.GetTypeInfoInternal(runtimeType, fallBackToNearestAncestorType: true);
                PolymorphicTypeInfo = typeInfo.AncestorPolymorphicType ?? typeInfo;
            }
 
            PolymorphicSerializationState = PolymorphicSerializationState.PolymorphicReEntryStarted;
            return PolymorphicTypeInfo;
        }
 
        /// <summary>
        /// Configures the next stack frame for a polymorphic converter.
        /// </summary>
        public JsonConverter InitializePolymorphicReEntry(JsonTypeInfo derivedJsonTypeInfo)
        {
            Debug.Assert(PolymorphicSerializationState is PolymorphicSerializationState.None or PolymorphicSerializationState.PolymorphicReEntryStarted);
 
            PolymorphicTypeInfo = derivedJsonTypeInfo;
            PolymorphicSerializationState = PolymorphicSerializationState.PolymorphicReEntryStarted;
            return derivedJsonTypeInfo.Converter;
        }
 
        /// <summary>
        /// Configures the next frame for a continuation of a polymorphic converter.
        /// </summary>
        public JsonConverter ResumePolymorphicReEntry()
        {
            Debug.Assert(PolymorphicSerializationState == PolymorphicSerializationState.PolymorphicReEntrySuspended);
            Debug.Assert(PolymorphicTypeInfo is not null);
            PolymorphicSerializationState = PolymorphicSerializationState.PolymorphicReEntryStarted;
            return PolymorphicTypeInfo.Converter;
        }
 
        /// <summary>
        /// Updates frame state after a polymorphic converter has returned.
        /// </summary>
        public void ExitPolymorphicConverter(bool success)
        {
            PolymorphicSerializationState = success ? PolymorphicSerializationState.None : PolymorphicSerializationState.PolymorphicReEntrySuspended;
        }
 
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private readonly string DebuggerDisplay => $"ConverterStrategy.{JsonTypeInfo?.Converter.ConverterStrategy}, {JsonTypeInfo?.Type.Name}";
    }
}