< Summary

Line coverage
21%
Covered lines: 83
Uncovered lines: 305
Coverable lines: 388
Total lines: 711
Line coverage: 21.3%
Branch coverage
12%
Covered branches: 13
Total branches: 107
Branch coverage: 12.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Serialization\Metadata\JsonTypeInfoOfT.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.ComponentModel;
 5using System.Diagnostics;
 6
 7namespace System.Text.Json.Serialization.Metadata
 8{
 9    /// <summary>
 10    /// Provides JSON serialization-related metadata about a type.
 11    /// </summary>
 12    /// <typeparam name="T">The generic definition of the type.</typeparam>
 13    public sealed partial class JsonTypeInfo<T> : JsonTypeInfo
 14    {
 15        private Action<Utf8JsonWriter, T>? _serialize;
 16
 17        private Func<T>? _typedCreateObject;
 18
 19        internal JsonTypeInfo(JsonConverter converter, JsonSerializerOptions options)
 1225420            : base(typeof(T), converter, options)
 1225421        {
 1225422            EffectiveConverter = converter.CreateCastingConverter<T>();
 1225423        }
 24
 25        /// <summary>
 26        /// A Converter whose declared type always matches that of the current JsonTypeInfo.
 27        /// It might be the same instance as JsonTypeInfo.Converter or it could be wrapped
 28        /// in a CastingConverter in cases where a polymorphic converter is being used.
 29        /// </summary>
 7620030        internal JsonConverter<T> EffectiveConverter { get; }
 31
 32        /// <summary>
 33        /// Gets or sets a parameterless factory to be used on deserialization.
 34        /// </summary>
 35        /// <exception cref="InvalidOperationException">
 36        /// The <see cref="JsonTypeInfo"/> instance has been locked for further modification.
 37        ///
 38        /// -or-
 39        ///
 40        /// A parameterless factory is not supported for the current metadata <see cref="JsonTypeInfo.Kind"/>.
 41        /// </exception>
 42        /// <remarks>
 43        /// If set to <see langword="null" />, any attempt to deserialize instances of the given type will fail at runti
 44        ///
 45        /// For contracts originating from <see cref="DefaultJsonTypeInfoResolver"/> or <see cref="JsonSerializerContext
 46        /// types with a single default constructor or default constructors annotated with <see cref="JsonConstructorAtt
 47        /// will be mapped to this delegate.
 48        /// </remarks>
 49        public new Func<T>? CreateObject
 50        {
 051            get => _typedCreateObject;
 52            set
 053            {
 054                SetCreateObject(value);
 055            }
 56        }
 57
 58        private protected override void SetCreateObject(Delegate? createObject)
 155259        {
 155260            Debug.Assert(createObject is null or Func<object> or Func<T>);
 61
 155262            VerifyMutable();
 63
 155264            if (Kind == JsonTypeInfoKind.None)
 065            {
 066                Debug.Assert(_createObject == null);
 067                Debug.Assert(_typedCreateObject == null);
 068                ThrowHelper.ThrowInvalidOperationException_JsonTypeInfoOperationNotPossibleForKind(Kind);
 69            }
 70
 155271            if (!Converter.SupportsCreateObjectDelegate)
 072            {
 073                Debug.Assert(_createObject is null);
 074                Debug.Assert(_typedCreateObject == null);
 075                ThrowHelper.ThrowInvalidOperationException_CreateObjectConverterNotCompatible(Type);
 76            }
 77
 78            Func<object>? untypedCreateObject;
 79            Func<T>? typedCreateObject;
 80
 155281            if (createObject is null)
 082            {
 083                untypedCreateObject = null;
 084                typedCreateObject = null;
 085            }
 155286            else if (createObject is Func<T> typedDelegate)
 087            {
 088                typedCreateObject = typedDelegate;
 089                untypedCreateObject = createObject is Func<object> untypedDelegate ? untypedDelegate : () => typedDelega
 090            }
 91            else
 155292            {
 155293                Debug.Assert(createObject is Func<object>);
 155294                untypedCreateObject = (Func<object>)createObject;
 155295                typedCreateObject = () => (T)untypedCreateObject();
 155296            }
 97
 155298            _createObject = untypedCreateObject;
 155299            _typedCreateObject = typedCreateObject;
 100
 101            // Clear any data related to the previously specified ctor
 1552102            ConstructorAttributeProviderFactory = null;
 1552103            ConstructorAttributeProvider = null;
 104
 1552105            if (CreateObjectWithArgs is not null)
 0106            {
 0107                _parameterInfoValuesIndex = null;
 0108                CreateObjectWithArgs = null;
 0109                ParameterCount = 0;
 110
 0111                foreach (JsonPropertyInfo propertyInfo in PropertyList)
 0112                {
 0113                    propertyInfo.AssociatedParameter = null;
 0114                }
 0115            }
 1552116        }
 117
 118        /// <summary>
 119        /// Serializes an instance of <typeparamref name="T"/> using
 120        /// <see cref="JsonSourceGenerationOptionsAttribute"/> values specified at design time.
 121        /// </summary>
 122        /// <remarks>The writer is not flushed after writing.</remarks>
 123        [EditorBrowsable(EditorBrowsableState.Never)]
 124        public Action<Utf8JsonWriter, T>? SerializeHandler
 125        {
 126            get
 0127            {
 0128                return _serialize;
 0129            }
 130            internal set
 0131            {
 0132                Debug.Assert(!IsReadOnly, "We should not mutate read-only JsonTypeInfo");
 0133                _serialize = value;
 0134                HasSerializeHandler = value != null;
 0135            }
 136        }
 137
 138        private protected override JsonPropertyInfo CreatePropertyInfoForTypeInfo()
 12254139        {
 12254140            return new JsonPropertyInfo<T>(
 12254141                declaringType: typeof(T),
 12254142                declaringTypeInfo: this,
 12254143                Options)
 12254144            {
 12254145                JsonTypeInfo = this,
 12254146                IsForTypeInfo = true,
 12254147            };
 12254148        }
 149
 150        private protected override JsonPropertyInfo CreateJsonPropertyInfo(JsonTypeInfo declaringTypeInfo, Type? declari
 6437151        {
 6437152            return new JsonPropertyInfo<T>(declaringType ?? declaringTypeInfo.Type, declaringTypeInfo, options)
 6437153            {
 6437154                JsonTypeInfo = this
 6437155            };
 6437156        }
 157    }
 158}

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Serialization\Metadata\JsonTypeInfoOfT.ReadHelper.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Collections.Generic;
 5using System.Diagnostics;
 6using System.IO;
 7using System.IO.Pipelines;
 8using System.Threading;
 9using System.Threading.Tasks;
 10
 11namespace System.Text.Json.Serialization.Metadata
 12{
 13    public partial class JsonTypeInfo<T>
 14    {
 15        // This section provides helper methods guiding root-level deserialization
 16        // of values corresponding according to the current JsonTypeInfo configuration.
 17
 18        internal T? Deserialize(ref Utf8JsonReader reader, ref ReadStack state)
 019        {
 020            Debug.Assert(IsConfigured);
 021            bool success = EffectiveConverter.ReadCore(ref reader, out T? result, Options, ref state);
 022            Debug.Assert(success, "Should only return false for async deserialization");
 023            return result;
 024        }
 25
 26        internal async ValueTask<T?> DeserializeAsync<TReadBufferState, TStream>(TStream utf8Json, TReadBufferState buff
 27            where TReadBufferState : struct, IReadBufferState<TReadBufferState, TStream>
 5592428        {
 5592429            Debug.Assert(IsConfigured);
 5592430            JsonSerializerOptions options = Options;
 5592431            ReadStack readStack = default;
 5592432            readStack.Initialize(this, supportContinuation: true);
 5592433            var jsonReaderState = new JsonReaderState(options.GetReaderOptions());
 34
 35            try
 5592436            {
 5592437                while (true)
 5592438                {
 5592439                    bufferState = await bufferState.ReadAsync(utf8Json, cancellationToken).ConfigureAwait(false);
 5592440                    bool success = ContinueDeserialize<TReadBufferState, TStream>(
 5592441                        ref bufferState,
 5592442                        ref jsonReaderState,
 5592443                        ref readStack,
 5592444                        out T? value);
 45
 17446                    if (success)
 17447                    {
 17448                        return value;
 49                    }
 050                }
 51            }
 52            finally
 5592453            {
 5592454                bufferState.Dispose();
 5592455            }
 17456        }
 57
 58        internal ValueTask<T?> DeserializeAsync(Stream utf8Json, CancellationToken cancellationToken)
 059        {
 60            // Note: The ReadBufferState ctor rents pooled buffers.
 061            StreamReadBufferState bufferState = new StreamReadBufferState(Options.DefaultBufferSize);
 062            return DeserializeAsync(utf8Json, bufferState, cancellationToken);
 063        }
 64
 65        internal ValueTask<T?> DeserializeAsync(PipeReader utf8Json, CancellationToken cancellationToken)
 066        {
 067            PipeReadBufferState bufferState = new(utf8Json);
 068            return DeserializeAsync(utf8Json, bufferState, cancellationToken);
 069        }
 70
 71        internal T? Deserialize(Stream utf8Json)
 072        {
 073            Debug.Assert(IsConfigured);
 074            JsonSerializerOptions options = Options;
 075            ReadStack readStack = default;
 076            readStack.Initialize(this, supportContinuation: true);
 077            var jsonReaderState = new JsonReaderState(options.GetReaderOptions());
 78            // Note: The ReadBufferState ctor rents pooled buffers.
 079            StreamReadBufferState bufferState = new StreamReadBufferState(options.DefaultBufferSize);
 80
 81            try
 082            {
 083                while (true)
 084                {
 085                    bufferState.Read(utf8Json);
 086                    bool success = ContinueDeserialize<StreamReadBufferState, Stream>(
 087                        ref bufferState,
 088                        ref jsonReaderState,
 089                        ref readStack,
 090                        out T? value);
 91
 092                    if (success)
 093                    {
 094                        return value;
 95                    }
 096                }
 97            }
 98            finally
 099            {
 0100                bufferState.Dispose();
 0101            }
 0102        }
 103
 104        /// <summary>
 105        /// Caches JsonTypeInfo&lt;List&lt;T&gt;&gt; instances used by the DeserializeAsyncEnumerable method.
 106        /// Store as a non-generic type to avoid triggering generic recursion in the AOT compiler.
 107        /// cf. https://github.com/dotnet/runtime/issues/85184
 108        /// </summary>
 109        internal JsonTypeInfo? _asyncEnumerableArrayTypeInfo;
 110        internal JsonTypeInfo? _asyncEnumerableRootLevelValueTypeInfo;
 111
 112        internal sealed override object? DeserializeAsObject(ref Utf8JsonReader reader, ref ReadStack state)
 0113            => Deserialize(ref reader, ref state);
 114
 115        internal sealed override async ValueTask<object?> DeserializeAsObjectAsync(Stream utf8Json, CancellationToken ca
 27962116        {
 117            // Note: The ReadBufferState ctor rents pooled buffers.
 27962118            StreamReadBufferState bufferState = new StreamReadBufferState(Options.DefaultBufferSize);
 27962119            T? result = await DeserializeAsync(utf8Json, bufferState, cancellationToken).ConfigureAwait(false);
 87120            return result;
 87121        }
 122
 123        internal sealed override async ValueTask<object?> DeserializeAsObjectAsync(PipeReader utf8Json, CancellationToke
 27962124        {
 27962125            T? result = await DeserializeAsync<PipeReadBufferState, PipeReader>(utf8Json, bufferState: new PipeReadBuffe
 87126            return result;
 87127        }
 128
 129        internal sealed override object? DeserializeAsObject(Stream utf8Json)
 0130            => Deserialize(utf8Json);
 131
 132        internal bool ContinueDeserialize<TReadBufferState, TStream>(
 133            ref TReadBufferState bufferState,
 134            ref JsonReaderState jsonReaderState,
 135            ref ReadStack readStack,
 136            out T? value)
 137            where TReadBufferState : struct, IReadBufferState<TReadBufferState, TStream>
 55924138        {
 55924139            bufferState.GetReader(jsonReaderState, out Utf8JsonReader reader);
 140
 141            try
 55924142            {
 55924143                bool success = EffectiveConverter.ReadCore(ref reader, out value, Options, ref readStack);
 144
 145#if DEBUG
 174146                Debug.Assert(reader.BytesConsumed <= bufferState.Bytes.Length);
 174147                Debug.Assert(!bufferState.IsFinalBlock || reader.AllowMultipleValues || reader.BytesConsumed == bufferSt
 174148                    "The reader should have thrown if we have remaining bytes.");
 149#endif
 150
 174151                jsonReaderState = reader.CurrentState;
 174152                return success;
 153            }
 154            finally
 55924155            {
 55924156                bufferState.Advance(reader.BytesConsumed);
 55924157            }
 174158        }
 159    }
 160}

C:\h\w\B31A098C\w\BB5A0A33\e\runtime-utils\Runner\runtime\src\libraries\System.Text.Json\src\System\Text\Json\Serialization\Metadata\JsonTypeInfoOfT.WriteHelpers.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Diagnostics;
 5using System.IO;
 6using System.IO.Pipelines;
 7using System.Text.Json.Serialization.Converters;
 8using System.Threading;
 9using System.Threading.Tasks;
 10
 11namespace System.Text.Json.Serialization.Metadata
 12{
 13    public partial class JsonTypeInfo<T>
 14    {
 15        // This section provides helper methods guiding root-level serialization
 16        // of values corresponding according to the current JsonTypeInfo configuration.
 17
 18        // Root serialization method for sync, non-streaming serialization
 19        internal void Serialize(
 20            Utf8JsonWriter writer,
 21            in T? rootValue,
 22            object? rootValueBoxed = null)
 023        {
 024            Debug.Assert(IsConfigured);
 025            Debug.Assert(rootValueBoxed is null || rootValueBoxed is T);
 26
 027            if (CanUseSerializeHandler)
 028            {
 29                // Short-circuit calls into SerializeHandler, if supported.
 30                // Even though this is already handled by JsonMetadataServicesConverter,
 31                // this avoids creating a WriteStack and calling into the converter infrastructure.
 32
 033                Debug.Assert(SerializeHandler != null);
 034                Debug.Assert(Converter is JsonMetadataServicesConverter<T>);
 35
 036                SerializeHandler(writer, rootValue!);
 037                writer.Flush();
 038            }
 039            else if (
 040#if NET
 041                !typeof(T).IsValueType &&
 042#endif
 043                Converter.CanBePolymorphic &&
 044                rootValue is not null &&
 045                Options.TryGetPolymorphicTypeInfoForRootType(rootValue, out JsonTypeInfo? derivedTypeInfo))
 046            {
 047                Debug.Assert(typeof(T) == typeof(object));
 048                derivedTypeInfo.SerializeAsObject(writer, rootValue);
 49                // NB flushing is handled by the derived type's serialization method.
 050            }
 51            else
 052            {
 053                WriteStack state = default;
 054                state.Initialize(this, rootValueBoxed);
 55
 056                bool success = EffectiveConverter.WriteCore(writer, rootValue, Options, ref state);
 057                Debug.Assert(success);
 058                writer.Flush();
 059            }
 060        }
 61
 62        internal Task SerializeAsync(Stream utf8Json,
 63            T? rootValue,
 64            CancellationToken cancellationToken,
 65            object? rootValueBoxed = null)
 066        {
 067            PooledByteBufferWriter writer = new PooledByteBufferWriter(Options.DefaultBufferSize, utf8Json);
 68            // Value chosen as 90% of the default buffer used in PooledByteBufferWriter.
 69            // This is a tradeoff between likelihood of needing to grow the array vs. utilizing most of the buffer
 070            int flushThreshold = (int)(writer.Capacity * JsonSerializer.FlushThreshold);
 071            return SerializeAsync(writer, rootValue, flushThreshold, cancellationToken, rootValueBoxed);
 072        }
 73
 74        internal Task SerializeAsync(PipeWriter utf8Json,
 75            T? rootValue,
 76            CancellationToken cancellationToken,
 77            object? rootValueBoxed = null)
 078        {
 79            // Value chosen as 90% of 4 buffer segments in Pipes. This is semi-arbitrarily chosen and may be changed in 
 080            int flushThreshold = (int)(4 * PipeOptions.Default.MinimumSegmentSize * JsonSerializer.FlushThreshold);
 081            return SerializeAsync(utf8Json, rootValue, flushThreshold, cancellationToken, rootValueBoxed);
 082        }
 83
 84        // Root serialization method for async streaming serialization.
 85        private async Task SerializeAsync(
 86            PipeWriter pipeWriter,
 87            T? rootValue,
 88            int flushThreshold,
 89            CancellationToken cancellationToken,
 90            object? rootValueBoxed = null)
 091        {
 092            Debug.Assert(IsConfigured);
 093            Debug.Assert(rootValueBoxed is null || rootValueBoxed is T);
 94
 095            if (CanUseSerializeHandlerInStreaming)
 096            {
 97                // Short-circuit calls into SerializeHandler, if the `CanUseSerializeHandlerInStreaming` heuristic allow
 98
 099                Debug.Assert(SerializeHandler != null);
 0100                Debug.Assert(CanUseSerializeHandler);
 0101                Debug.Assert(Converter is JsonMetadataServicesConverter<T>);
 102
 0103                Utf8JsonWriter writer = Utf8JsonWriterCache.RentWriter(Options, pipeWriter);
 104
 105                try
 0106                {
 107                    try
 0108                    {
 0109                        SerializeHandler(writer, rootValue!);
 0110                        writer.Flush();
 0111                    }
 112                    finally
 0113                    {
 114                        // Record the serialization size in both successful and failed operations,
 115                        // since we want to immediately opt out of the fast path if it exceeds the threshold.
 0116                        OnRootLevelAsyncSerializationCompleted(writer.BytesCommitted + writer.BytesPending);
 117
 0118                        Utf8JsonWriterCache.ReturnWriter(writer);
 0119                    }
 120
 0121                    FlushResult result = await pipeWriter.FlushAsync(cancellationToken).ConfigureAwait(false);
 0122                    if (result.IsCanceled)
 0123                    {
 0124                        ThrowHelper.ThrowOperationCanceledException_PipeWriteCanceled();
 0125                    }
 0126                }
 127                finally
 0128                {
 0129                    if (pipeWriter is PooledByteBufferWriter disposable)
 0130                    {
 0131                        disposable.Dispose();
 0132                    }
 0133                }
 0134            }
 0135            else if (
 0136#if NET
 0137                !typeof(T).IsValueType &&
 0138#endif
 0139                Converter.CanBePolymorphic &&
 0140                rootValue is not null &&
 0141                Options.TryGetPolymorphicTypeInfoForRootType(rootValue, out JsonTypeInfo? derivedTypeInfo))
 0142            {
 0143                Debug.Assert(typeof(T) == typeof(object));
 0144                await derivedTypeInfo.SerializeAsObjectAsync(pipeWriter, rootValue, flushThreshold, cancellationToken).C
 0145            }
 146            else
 0147            {
 148                bool isFinalBlock;
 0149                WriteStack state = default;
 0150                state.Initialize(this,
 0151                    rootValueBoxed,
 0152                    supportContinuation: true,
 0153                    supportAsync: true);
 154
 0155                if (!pipeWriter.CanGetUnflushedBytes)
 0156                {
 0157                    ThrowHelper.ThrowInvalidOperationException_PipeWriterDoesNotImplementUnflushedBytes(pipeWriter);
 0158                }
 0159                state.PipeWriter = pipeWriter;
 0160                state.CancellationToken = cancellationToken;
 161
 0162                var writer = new Utf8JsonWriter(pipeWriter, Options.GetWriterOptions());
 163
 164                try
 0165                {
 0166                    state.FlushThreshold = flushThreshold;
 167
 168                    do
 0169                    {
 170                        try
 0171                        {
 0172                            isFinalBlock = EffectiveConverter.WriteCore(writer, rootValue, Options, ref state);
 173
 0174                            if (state.SuppressFlush)
 0175                            {
 0176                                Debug.Assert(!isFinalBlock);
 0177                                Debug.Assert(state.PendingTask is not null);
 0178                                state.SuppressFlush = false;
 0179                            }
 180                            else
 0181                            {
 0182                                writer.Flush();
 0183                                FlushResult result = await pipeWriter.FlushAsync(cancellationToken).ConfigureAwait(false
 0184                                if (result.IsCanceled || result.IsCompleted)
 0185                                {
 0186                                    if (result.IsCanceled)
 0187                                    {
 0188                                        ThrowHelper.ThrowOperationCanceledException_PipeWriteCanceled();
 0189                                    }
 190
 191                                    // Pipe is completed, no one is reading so no point in continuing serialization
 0192                                    return;
 193                                }
 0194                            }
 0195                        }
 196                        finally
 0197                        {
 198                            // Await any pending resumable converter tasks (currently these can only be IAsyncEnumerator
 199                            // Note that pending tasks are always awaited, even if an exception has been thrown or the c
 0200                            if (state.PendingTask is not null)
 0201                            {
 202                                // Exceptions should only be propagated by the resuming converter
 203#if NET
 0204                                await state.PendingTask.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);
 205#else
 206                                try
 207                                {
 208                                    await state.PendingTask.ConfigureAwait(false);
 209                                }
 210                                catch { }
 211#endif
 0212                            }
 213
 214                            // Dispose any pending async disposables (currently these can only be completed IAsyncEnumer
 0215                            if (state.CompletedAsyncDisposables?.Count > 0)
 0216                            {
 0217                                await state.DisposeCompletedAsyncDisposables().ConfigureAwait(false);
 0218                            }
 0219                        }
 220
 0221                    } while (!isFinalBlock);
 222
 0223                    if (CanUseSerializeHandler)
 0224                    {
 225                        // On successful serialization, record the serialization size
 226                        // to determine potential suitability of the type for
 227                        // fast-path serialization in streaming methods.
 0228                        Debug.Assert(writer.BytesPending == 0);
 0229                        OnRootLevelAsyncSerializationCompleted(writer.BytesCommitted);
 0230                    }
 0231                }
 0232                catch
 0233                {
 234                    // Reset the writer in exception cases as we don't want the writer.Dispose() call to flush any pendi
 0235                    writer.Reset();
 0236                    writer.Dispose();
 237                    // On exception, walk the WriteStack for any orphaned disposables and try to dispose them.
 0238                    await state.DisposePendingDisposablesOnExceptionAsync().ConfigureAwait(false);
 0239                    throw;
 240                }
 241                finally
 242                {
 0243                    writer.Dispose();
 0244                    if (pipeWriter is PooledByteBufferWriter disposable)
 245                    {
 0246                        disposable.Dispose();
 247                    }
 0248                }
 0249            }
 0250        }
 251
 252        // Root serialization method for non-async streaming serialization
 253        internal void Serialize(
 254            Stream utf8Json,
 255            in T? rootValue,
 256            object? rootValueBoxed = null)
 0257        {
 0258            Debug.Assert(IsConfigured);
 0259            Debug.Assert(rootValueBoxed is null || rootValueBoxed is T);
 260
 0261            if (CanUseSerializeHandlerInStreaming)
 0262            {
 263                // Short-circuit calls into SerializeHandler, if the `CanUseSerializeHandlerInStreaming` heuristic allow
 264
 0265                Debug.Assert(SerializeHandler != null);
 0266                Debug.Assert(CanUseSerializeHandler);
 0267                Debug.Assert(Converter is JsonMetadataServicesConverter<T>);
 268
 0269                Utf8JsonWriter writer = Utf8JsonWriterCache.RentWriterAndBuffer(Options, out PooledByteBufferWriter buff
 270                try
 0271                {
 0272                    SerializeHandler(writer, rootValue!);
 0273                    writer.Flush();
 0274                    bufferWriter.WriteToStream(utf8Json);
 0275                }
 276                finally
 0277                {
 278                    // Record the serialization size in both successful and failed operations,
 279                    // since we want to immediately opt out of the fast path if it exceeds the threshold.
 0280                    OnRootLevelAsyncSerializationCompleted(writer.BytesCommitted + writer.BytesPending);
 281
 0282                    Utf8JsonWriterCache.ReturnWriterAndBuffer(writer, bufferWriter);
 0283                }
 0284            }
 0285            else if (
 0286#if NET
 0287                !typeof(T).IsValueType &&
 0288#endif
 0289                Converter.CanBePolymorphic &&
 0290                rootValue is not null &&
 0291                Options.TryGetPolymorphicTypeInfoForRootType(rootValue, out JsonTypeInfo? polymorphicTypeInfo))
 0292            {
 0293                Debug.Assert(typeof(T) == typeof(object));
 0294                polymorphicTypeInfo.SerializeAsObject(utf8Json, rootValue);
 0295            }
 296            else
 0297            {
 298                bool isFinalBlock;
 0299                WriteStack state = default;
 0300                state.Initialize(this,
 0301                    rootValueBoxed,
 0302                    supportContinuation: true,
 0303                    supportAsync: false);
 304
 0305                Utf8JsonWriter writer = Utf8JsonWriterCache.RentWriterAndBuffer(Options, out PooledByteBufferWriter buff
 0306                Debug.Assert(bufferWriter.CanGetUnflushedBytes);
 307
 308                try
 0309                {
 0310                    state.PipeWriter = bufferWriter;
 0311                    state.FlushThreshold = (int)(bufferWriter.Capacity * JsonSerializer.FlushThreshold);
 312
 313                    do
 0314                    {
 0315                        isFinalBlock = EffectiveConverter.WriteCore(writer, rootValue, Options, ref state);
 0316                        writer.Flush();
 317
 0318                        bufferWriter.WriteToStream(utf8Json);
 0319                        bufferWriter.Clear();
 320
 0321                        Debug.Assert(state.PendingTask == null);
 0322                    } while (!isFinalBlock);
 323
 0324                    if (CanUseSerializeHandler)
 0325                    {
 326                        // On successful serialization, record the serialization size
 327                        // to determine potential suitability of the type for
 328                        // fast-path serialization in streaming methods.
 0329                        Debug.Assert(writer.BytesPending == 0);
 0330                        OnRootLevelAsyncSerializationCompleted(writer.BytesCommitted);
 0331                    }
 0332                }
 333                finally
 0334                {
 0335                    Utf8JsonWriterCache.ReturnWriterAndBuffer(writer, bufferWriter);
 0336                }
 0337            }
 0338        }
 339
 340        internal sealed override void SerializeAsObject(Utf8JsonWriter writer, object? rootValue)
 0341            => Serialize(writer, JsonSerializer.UnboxOnWrite<T>(rootValue), rootValue);
 342
 343        internal sealed override Task SerializeAsObjectAsync(PipeWriter pipeWriter, object? rootValue, int flushThreshol
 0344            => SerializeAsync(pipeWriter, JsonSerializer.UnboxOnWrite<T>(rootValue), flushThreshold, cancellationToken, 
 345
 346        internal sealed override Task SerializeAsObjectAsync(Stream utf8Json, object? rootValue, CancellationToken cance
 0347            => SerializeAsync(utf8Json, JsonSerializer.UnboxOnWrite<T>(rootValue), cancellationToken, rootValue);
 348
 349        internal sealed override Task SerializeAsObjectAsync(PipeWriter utf8Json, object? rootValue, CancellationToken c
 0350            => SerializeAsync(utf8Json, JsonSerializer.UnboxOnWrite<T>(rootValue), cancellationToken, rootValue);
 351
 352        internal sealed override void SerializeAsObject(Stream utf8Json, object? rootValue)
 0353            => Serialize(utf8Json, JsonSerializer.UnboxOnWrite<T>(rootValue), rootValue);
 354
 355        // Fast-path serialization in source gen has not been designed with streaming in mind.
 356        // Even though it's not used in streaming by default, we can sometimes try to turn it on
 357        // assuming that the current type is known to produce small enough JSON payloads.
 358        // The `CanUseSerializeHandlerInStreaming` flag returns true iff:
 359        //  * The type has been used in at least `MinSerializationsSampleSize` streaming serializations AND
 360        //  * No serialization size exceeding JsonSerializerOptions.DefaultBufferSize / 2 has been recorded so far.
 0361        private bool CanUseSerializeHandlerInStreaming => _canUseSerializeHandlerInStreamingState == 1;
 362        private volatile int _canUseSerializeHandlerInStreamingState; // 0: unspecified, 1: allowed, 2: forbidden
 363
 364        private const int MinSerializationsSampleSize = 10;
 365        private volatile int _serializationCount;
 366
 367        // Samples the latest serialization size for the current type to determine
 368        // if the fast-path SerializeHandler is appropriate for streaming serialization.
 369        private void OnRootLevelAsyncSerializationCompleted(long serializationSize)
 0370        {
 0371            Debug.Assert(CanUseSerializeHandler);
 372
 0373            if (_canUseSerializeHandlerInStreamingState != 2)
 0374            {
 0375                if ((ulong)serializationSize > (ulong)(Options.DefaultBufferSize / 2))
 0376                {
 377                    // We have a serialization that exceeds the buffer size --
 378                    // forbid any use future use of the fast-path handler.
 0379                    _canUseSerializeHandlerInStreamingState = 2;
 0380                }
 0381                else if ((uint)_serializationCount < MinSerializationsSampleSize)
 0382                {
 0383                    if (Interlocked.Increment(ref _serializationCount) == MinSerializationsSampleSize)
 0384                    {
 385                        // We have the minimum number of serializations needed to flag the type as safe for fast-path.
 386                        // Use CMPXCHG to avoid racing with threads reporting a large serialization.
 0387                        Interlocked.CompareExchange(ref _canUseSerializeHandlerInStreamingState, 1, 0);
 0388                    }
 0389                }
 0390            }
 0391        }
 392    }
 393}

Methods/Properties

.ctor(System.Text.Json.Serialization.JsonConverter,System.Text.Json.JsonSerializerOptions)
EffectiveConverter()
CreateObject()
CreateObject(System.Func`1<T>)
SetCreateObject(System.Delegate)
SerializeHandler()
SerializeHandler(System.Action`2<System.Text.Json.Utf8JsonWriter,T>)
CreatePropertyInfoForTypeInfo()
CreateJsonPropertyInfo(System.Text.Json.Serialization.Metadata.JsonTypeInfo,System.Type,System.Text.Json.JsonSerializerOptions)
Deserialize(System.Text.Json.Utf8JsonReader&,System.Text.Json.ReadStack&)
DeserializeAsync()
DeserializeAsync(System.IO.Stream,System.Threading.CancellationToken)
DeserializeAsync(System.IO.Pipelines.PipeReader,System.Threading.CancellationToken)
Deserialize(System.IO.Stream)
DeserializeAsObject(System.Text.Json.Utf8JsonReader&,System.Text.Json.ReadStack&)
DeserializeAsObjectAsync()
DeserializeAsObjectAsync()
DeserializeAsObject(System.IO.Stream)
ContinueDeserialize(TReadBufferState&,System.Text.Json.JsonReaderState&,System.Text.Json.ReadStack&,T&)
Serialize(System.Text.Json.Utf8JsonWriter,T&,System.Object)
SerializeAsync(System.IO.Stream,T,System.Threading.CancellationToken,System.Object)
SerializeAsync(System.IO.Pipelines.PipeWriter,T,System.Threading.CancellationToken,System.Object)
SerializeAsync()
Serialize(System.IO.Stream,T&,System.Object)
SerializeAsObject(System.Text.Json.Utf8JsonWriter,System.Object)
SerializeAsObjectAsync(System.IO.Pipelines.PipeWriter,System.Object,System.Int32,System.Threading.CancellationToken)
SerializeAsObjectAsync(System.IO.Stream,System.Object,System.Threading.CancellationToken)
SerializeAsObjectAsync(System.IO.Pipelines.PipeWriter,System.Object,System.Threading.CancellationToken)
SerializeAsObject(System.IO.Stream,System.Object)
CanUseSerializeHandlerInStreaming()
OnRootLevelAsyncSerializationCompleted(System.Int64)