File: System\Text\Json\Serialization\ReadStackFrame.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.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
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 ReadStackFrame
    {
        // Current property values.
        public JsonPropertyInfo? JsonPropertyInfo;
        public StackFramePropertyState PropertyState;
        public bool UseExtensionProperty;
 
        // Support JSON Path on exceptions and non-string Dictionary keys.
        // This is Utf8 since we don't want to convert to string until an exception is thrown.
        // For dictionary keys we don't want to convert to TKey until we have both key and value when parsing the dictionary elements on stream cases.
        public byte[]? JsonPropertyName;
        public string? JsonPropertyNameAsString; // This is used for string dictionary keys and re-entry cases that specify a property name.
 
        // Stores the non-string dictionary keys for continuation.
        public object? DictionaryKey;
 
        /// <summary>
        /// Records the Utf8JsonReader Depth at the start of the current value.
        /// </summary>
        public int OriginalDepth;
#if DEBUG
        /// <summary>
        /// Records the Utf8JsonReader TokenType at the start of the current value.
        /// Only used to validate debug builds.
        /// </summary>
        public JsonTokenType OriginalTokenType;
#endif
 
        // Current object (POCO or IEnumerable).
        public object? ReturnValue; // The current return value used for re-entry.
        public JsonTypeInfo JsonTypeInfo;
        public StackFrameObjectState ObjectState; // State tracking the current object.
 
        // Current object can contain metadata
        public bool CanContainMetadata;
        public MetadataPropertyName LatestMetadataPropertyName;
        public MetadataPropertyName MetadataPropertyNames;
 
        // Serialization state for value serialized by the current frame.
        public PolymorphicSerializationState PolymorphicSerializationState;
 
        // Holds any entered polymorphic JsonTypeInfo metadata.
        public JsonTypeInfo? PolymorphicJsonTypeInfo;
 
        // Gets the initial JsonTypeInfo metadata used when deserializing the current value.
        public JsonTypeInfo BaseJsonTypeInfo
            => PolymorphicSerializationState == PolymorphicSerializationState.PolymorphicReEntryStarted
                ? PolymorphicJsonTypeInfo!
                : JsonTypeInfo;
 
        // For performance, we order the properties by the first deserialize and PropertyIndex helps find the right slot quicker.
        public int PropertyIndex;
 
        // Tracks newly encounentered UTF-8 encoded properties during the current deserialization, to be appended to the cache.
        public PropertyRefCacheBuilder? PropertyRefCacheBuilder;
 
        // Holds relevant state when deserializing objects with parameterized constructors.
        public ArgumentState? CtorArgumentState;
 
        // Whether to use custom number handling.
        public JsonNumberHandling? NumberHandling;
 
        // Represents required properties which have value assigned.
        // Each bit corresponds to a required property.
        // False means that property is not set (not yet occurred in the payload).
        // Length of the BitArray is equal to number of required properties.
        // Every required JsonPropertyInfo has RequiredPropertyIndex property which maps to an index in this BitArray.
        public BitArray? RequiredPropertiesSet;
 
        // Tracks state related to property population.
        public bool HasParentObject;
        public bool IsPopulating;
 
        public void EndConstructorParameter()
        {
            CtorArgumentState!.JsonParameterInfo = null;
            JsonPropertyName = null;
            PropertyState = StackFramePropertyState.None;
        }
 
        public void EndProperty()
        {
            JsonPropertyInfo = null!;
            JsonPropertyName = null;
            JsonPropertyNameAsString = null;
            PropertyState = StackFramePropertyState.None;
 
            // No need to clear these since they are overwritten each time:
            //  NumberHandling
            //  UseExtensionProperty
        }
 
        public void EndElement()
        {
            JsonPropertyNameAsString = null;
            PropertyState = StackFramePropertyState.None;
        }
 
        /// <summary>
        /// Is the current object a Dictionary.
        /// </summary>
        public bool IsProcessingDictionary()
        {
            return JsonTypeInfo.Kind is JsonTypeInfoKind.Dictionary;
        }
 
        /// <summary>
        /// Is the current object an Enumerable.
        /// </summary>
        public bool IsProcessingEnumerable()
        {
            return JsonTypeInfo.Kind is JsonTypeInfoKind.Enumerable;
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void MarkRequiredPropertyAsRead(JsonPropertyInfo propertyInfo)
        {
            if (propertyInfo.IsRequired)
            {
                Debug.Assert(RequiredPropertiesSet != null);
                RequiredPropertiesSet[propertyInfo.RequiredPropertyIndex] = true;
            }
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal void InitializeRequiredPropertiesValidationState(JsonTypeInfo typeInfo)
        {
            Debug.Assert(RequiredPropertiesSet == null);
 
            if (typeInfo.NumberOfRequiredProperties > 0)
            {
                RequiredPropertiesSet = new BitArray(typeInfo.NumberOfRequiredProperties);
            }
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal void ValidateAllRequiredPropertiesAreRead(JsonTypeInfo typeInfo)
        {
            if (typeInfo.NumberOfRequiredProperties > 0)
            {
                Debug.Assert(RequiredPropertiesSet != null);
 
                if (!RequiredPropertiesSet.HasAllSet())
                {
                    ThrowHelper.ThrowJsonException_JsonRequiredPropertyMissing(typeInfo, RequiredPropertiesSet);
                }
            }
        }
 
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private string DebuggerDisplay => $"ConverterStrategy.{JsonTypeInfo?.Converter.ConverterStrategy}, {JsonTypeInfo?.Type.Name}";
    }
}