From 4892e4f581525dfe1ec3af9cfd3ce17e3cc32b52 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Mon, 22 Nov 2021 20:11:44 +0000 Subject: [PATCH] Add a JsonWriterOptions.MaxDepth property (#61608) * Add a JsonWriterOptions.MaxDepth property * remove depth checks from the converter layer * Revert "remove depth checks from the converter layer" This reverts commit 0e430928c8d84288e0b2cb2e1d1726087f905ae8. --- .../System.Text.Json/ref/System.Text.Json.cs | 1 + .../src/System/Text/Json/JsonConstants.cs | 1 - .../Serialization/JsonSerializerOptions.cs | 12 ++-- .../src/System/Text/Json/ThrowHelper.cs | 30 ++++----- .../Text/Json/Writer/JsonWriterOptions.cs | 24 +++++++ .../Utf8JsonWriter.WriteProperties.Bytes.cs | 4 +- ...Utf8JsonWriter.WriteProperties.DateTime.cs | 4 +- ...onWriter.WriteProperties.DateTimeOffset.cs | 4 +- .../Utf8JsonWriter.WriteProperties.Decimal.cs | 4 +- .../Utf8JsonWriter.WriteProperties.Double.cs | 4 +- .../Utf8JsonWriter.WriteProperties.Float.cs | 4 +- .../Utf8JsonWriter.WriteProperties.Guid.cs | 4 +- .../Utf8JsonWriter.WriteProperties.Helpers.cs | 20 +++--- .../Utf8JsonWriter.WriteProperties.Literal.cs | 4 +- ...JsonWriter.WriteProperties.SignedNumber.cs | 4 +- .../Utf8JsonWriter.WriteProperties.String.cs | 12 ++-- ...onWriter.WriteProperties.UnsignedNumber.cs | 4 +- .../Utf8JsonWriter.WriteValues.Bytes.cs | 2 +- .../Utf8JsonWriter.WriteValues.Comment.cs | 4 +- .../Utf8JsonWriter.WriteValues.DateTime.cs | 2 +- ...f8JsonWriter.WriteValues.DateTimeOffset.cs | 2 +- .../Utf8JsonWriter.WriteValues.Decimal.cs | 2 +- .../Utf8JsonWriter.WriteValues.Double.cs | 2 +- .../Utf8JsonWriter.WriteValues.Float.cs | 2 +- ...8JsonWriter.WriteValues.FormattedNumber.cs | 2 +- .../Writer/Utf8JsonWriter.WriteValues.Guid.cs | 2 +- .../Utf8JsonWriter.WriteValues.Helpers.cs | 4 +- .../Utf8JsonWriter.WriteValues.Literal.cs | 2 +- ...Utf8JsonWriter.WriteValues.SignedNumber.cs | 2 +- .../Utf8JsonWriter.WriteValues.String.cs | 4 +- ...f8JsonWriter.WriteValues.UnsignedNumber.cs | 2 +- .../System/Text/Json/Writer/Utf8JsonWriter.cs | 29 +++++--- .../JsonWriterOptionsTests.cs | 29 +++++--- .../Serialization/ReadValueTests.cs | 2 +- .../Serialization/WriteValueTests.cs | 66 +++++++++++++++++++ .../Utf8JsonWriterTests.cs | 53 +++++++++++++++ 36 files changed, 262 insertions(+), 91 deletions(-) diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index 1cde69c8ace1a1..f6b25cdcb23d41 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -358,6 +358,7 @@ public partial struct JsonWriterOptions private int _dummyPrimitive; public System.Text.Encodings.Web.JavaScriptEncoder? Encoder { readonly get { throw null; } set { } } public bool Indented { get { throw null; } set { } } + public int MaxDepth { readonly get { throw null; } set { } } public bool SkipValidation { get { throw null; } set { } } } public ref partial struct Utf8JsonReader diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/JsonConstants.cs b/src/libraries/System.Text.Json/src/System/Text/Json/JsonConstants.cs index f004e8d5d83474..bed577622f5faa 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/JsonConstants.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/JsonConstants.cs @@ -48,7 +48,6 @@ internal static partial class JsonConstants public static ReadOnlySpan EscapableChars => new byte[] { Quote, (byte)'n', (byte)'r', (byte)'t', Slash, (byte)'u', (byte)'b', (byte)'f' }; public const int SpacesPerIndent = 2; - public const int MaxWriterDepth = 1_000; public const int RemoveFlagsBitMask = 0x7FFFFFFF; public const int StackallocByteThreshold = 256; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs index 79c72342f78efd..4ddf4db675d421 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs @@ -20,6 +20,10 @@ public sealed partial class JsonSerializerOptions { internal const int BufferSizeDefault = 16 * 1024; + // For backward compatibility the default max depth for JsonSerializer is 64, + // the minimum of JsonReaderOptions.DefaultMaxDepth and JsonWriterOptions.DefaultMaxDepth. + internal const int DefaultMaxDepth = JsonReaderOptions.DefaultMaxDepth; + /// /// Gets a read-only, singleton instance of that uses the default configuration. /// @@ -433,12 +437,11 @@ public int MaxDepth } _maxDepth = value; - EffectiveMaxDepth = (value == 0 ? JsonReaderOptions.DefaultMaxDepth : value); + EffectiveMaxDepth = (value == 0 ? DefaultMaxDepth : value); } } - // The default is 64 because that is what the reader uses, so re-use the same JsonReaderOptions.DefaultMaxDepth constant. - internal int EffectiveMaxDepth { get; private set; } = JsonReaderOptions.DefaultMaxDepth; + internal int EffectiveMaxDepth { get; private set; } = DefaultMaxDepth; /// /// Specifies the policy used to convert a property's name on an object to another format, such as camel-casing. @@ -699,7 +702,7 @@ internal JsonReaderOptions GetReaderOptions() { AllowTrailingCommas = AllowTrailingCommas, CommentHandling = ReadCommentHandling, - MaxDepth = MaxDepth + MaxDepth = EffectiveMaxDepth }; } @@ -709,6 +712,7 @@ internal JsonWriterOptions GetWriterOptions() { Encoder = Encoder, Indented = WriteIndented, + MaxDepth = EffectiveMaxDepth, #if !DEBUG SkipValidation = true #endif diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs index 092588cee842a9..2721f685ae4dc6 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs @@ -126,12 +126,12 @@ public static void ThrowArgumentException(ReadOnlySpan propertyName, ReadO } [DoesNotReturn] - public static void ThrowInvalidOperationOrArgumentException(ReadOnlySpan propertyName, int currentDepth) + public static void ThrowInvalidOperationOrArgumentException(ReadOnlySpan propertyName, int currentDepth, int maxDepth) { currentDepth &= JsonConstants.RemoveFlagsBitMask; - if (currentDepth >= JsonConstants.MaxWriterDepth) + if (currentDepth >= maxDepth) { - ThrowInvalidOperationException(SR.Format(SR.DepthTooLarge, currentDepth, JsonConstants.MaxWriterDepth)); + ThrowInvalidOperationException(SR.Format(SR.DepthTooLarge, currentDepth, maxDepth)); } else { @@ -141,11 +141,11 @@ public static void ThrowInvalidOperationOrArgumentException(ReadOnlySpan p } [DoesNotReturn] - public static void ThrowInvalidOperationException(int currentDepth) + public static void ThrowInvalidOperationException(int currentDepth, int maxDepth) { currentDepth &= JsonConstants.RemoveFlagsBitMask; - Debug.Assert(currentDepth >= JsonConstants.MaxWriterDepth); - ThrowInvalidOperationException(SR.Format(SR.DepthTooLarge, currentDepth, JsonConstants.MaxWriterDepth)); + Debug.Assert(currentDepth >= maxDepth); + ThrowInvalidOperationException(SR.Format(SR.DepthTooLarge, currentDepth, maxDepth)); } [DoesNotReturn] @@ -183,12 +183,12 @@ private static InvalidOperationException GetInvalidOperationException(int curren } [DoesNotReturn] - public static void ThrowInvalidOperationOrArgumentException(ReadOnlySpan propertyName, int currentDepth) + public static void ThrowInvalidOperationOrArgumentException(ReadOnlySpan propertyName, int currentDepth, int maxDepth) { currentDepth &= JsonConstants.RemoveFlagsBitMask; - if (currentDepth >= JsonConstants.MaxWriterDepth) + if (currentDepth >= maxDepth) { - ThrowInvalidOperationException(SR.Format(SR.DepthTooLarge, currentDepth, JsonConstants.MaxWriterDepth)); + ThrowInvalidOperationException(SR.Format(SR.DepthTooLarge, currentDepth, maxDepth)); } else { @@ -433,9 +433,9 @@ private static string GetResourceString(ref Utf8JsonReader json, ExceptionResour } [DoesNotReturn] - public static void ThrowInvalidOperationException(ExceptionResource resource, int currentDepth, byte token, JsonTokenType tokenType) + public static void ThrowInvalidOperationException(ExceptionResource resource, int currentDepth, int maxDepth, byte token, JsonTokenType tokenType) { - throw GetInvalidOperationException(resource, currentDepth, token, tokenType); + throw GetInvalidOperationException(resource, currentDepth, maxDepth, token, tokenType); } [DoesNotReturn] @@ -508,9 +508,9 @@ public static InvalidOperationException GetInvalidOperationException(string mess } [MethodImpl(MethodImplOptions.NoInlining)] - public static InvalidOperationException GetInvalidOperationException(ExceptionResource resource, int currentDepth, byte token, JsonTokenType tokenType) + public static InvalidOperationException GetInvalidOperationException(ExceptionResource resource, int currentDepth, int maxDepth, byte token, JsonTokenType tokenType) { - string message = GetResourceString(resource, currentDepth, token, tokenType); + string message = GetResourceString(resource, currentDepth, maxDepth, token, tokenType); InvalidOperationException ex = GetInvalidOperationException(message); ex.Source = ExceptionSourceValueToRethrowAsJsonException; return ex; @@ -524,7 +524,7 @@ public static void ThrowOutOfMemoryException(uint capacity) // This function will convert an ExceptionResource enum value to the resource string. [MethodImpl(MethodImplOptions.NoInlining)] - private static string GetResourceString(ExceptionResource resource, int currentDepth, byte token, JsonTokenType tokenType) + private static string GetResourceString(ExceptionResource resource, int currentDepth, int maxDepth, byte token, JsonTokenType tokenType) { string message = ""; switch (resource) @@ -536,7 +536,7 @@ private static string GetResourceString(ExceptionResource resource, int currentD SR.Format(SR.MismatchedObjectArray, (char)token); break; case ExceptionResource.DepthTooLarge: - message = SR.Format(SR.DepthTooLarge, currentDepth & JsonConstants.RemoveFlagsBitMask, JsonConstants.MaxWriterDepth); + message = SR.Format(SR.DepthTooLarge, currentDepth & JsonConstants.RemoveFlagsBitMask, maxDepth); break; case ExceptionResource.CannotStartObjectArrayWithoutProperty: message = SR.Format(SR.CannotStartObjectArrayWithoutProperty, tokenType); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterOptions.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterOptions.cs index 1c60e5939fa469..e71668b8543232 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterOptions.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterOptions.cs @@ -13,6 +13,9 @@ namespace System.Text.Json /// public struct JsonWriterOptions { + internal const int DefaultMaxDepth = 1000; + + private int _maxDepth; private int _optionsMask; /// @@ -40,6 +43,27 @@ public bool Indented } } + /// + /// Gets or sets the maximum depth allowed when writing JSON, with the default (i.e. 0) indicating a max depth of 1000. + /// + /// + /// Thrown when the max depth is set to a negative value. + /// + /// + /// Reading past this depth will throw a . + /// + public int MaxDepth + { + readonly get => _maxDepth; + set + { + if (value < 0) + throw ThrowHelper.GetArgumentOutOfRangeException_MaxDepthMustBePositive(nameof(value)); + + _maxDepth = value; + } + } + /// /// Defines whether the should skip structural validation and allow /// the user to write invalid JSON, when set to true. If set to false, any attempts to write invalid JSON will result in diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Bytes.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Bytes.cs index 55d8aaa515deec..7ea9b37514cbaf 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Bytes.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Bytes.cs @@ -276,7 +276,7 @@ private void WriteBase64Minimized(ReadOnlySpan escapedPropertyName, ReadOn private void WriteBase64Indented(ReadOnlySpan escapedPropertyName, ReadOnlySpan bytes) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); int encodedLength = Base64.GetMaxEncodedToUtf8Length(bytes.Length); @@ -326,7 +326,7 @@ private void WriteBase64Indented(ReadOnlySpan escapedPropertyName, ReadOnl private void WriteBase64Indented(ReadOnlySpan escapedPropertyName, ReadOnlySpan bytes) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); int encodedLength = Base64.GetMaxEncodedToUtf8Length(bytes.Length); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTime.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTime.cs index e41427cdcdd8cf..77b8975332cd96 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTime.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTime.cs @@ -278,7 +278,7 @@ private void WriteStringMinimized(ReadOnlySpan escapedPropertyName, DateTi private void WriteStringIndented(ReadOnlySpan escapedPropertyName, DateTime value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - JsonConstants.MaximumFormatDateTimeOffsetLength - 7 - s_newLineLength); @@ -327,7 +327,7 @@ private void WriteStringIndented(ReadOnlySpan escapedPropertyName, DateTim private void WriteStringIndented(ReadOnlySpan escapedPropertyName, DateTime value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - JsonConstants.MaximumFormatDateTimeOffsetLength - 7 - s_newLineLength); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTimeOffset.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTimeOffset.cs index 313496df38c9b9..6b1013d9868ccf 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTimeOffset.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.DateTimeOffset.cs @@ -277,7 +277,7 @@ private void WriteStringMinimized(ReadOnlySpan escapedPropertyName, DateTi private void WriteStringIndented(ReadOnlySpan escapedPropertyName, DateTimeOffset value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - JsonConstants.MaximumFormatDateTimeOffsetLength - 7 - s_newLineLength); @@ -326,7 +326,7 @@ private void WriteStringIndented(ReadOnlySpan escapedPropertyName, DateTim private void WriteStringIndented(ReadOnlySpan escapedPropertyName, DateTimeOffset value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - JsonConstants.MaximumFormatDateTimeOffsetLength - 7 - s_newLineLength); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Decimal.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Decimal.cs index ab98e246fab684..ef2e614549e783 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Decimal.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Decimal.cs @@ -271,7 +271,7 @@ private void WriteNumberMinimized(ReadOnlySpan escapedPropertyName, decima private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, decimal value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - JsonConstants.MaximumFormatDecimalLength - 5 - s_newLineLength); @@ -317,7 +317,7 @@ private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, decimal private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, decimal value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - JsonConstants.MaximumFormatDecimalLength - 5 - s_newLineLength); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Double.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Double.cs index 1fb6832ce6ad3f..658267052137a0 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Double.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Double.cs @@ -275,7 +275,7 @@ private void WriteNumberMinimized(ReadOnlySpan escapedPropertyName, double private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, double value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - JsonConstants.MaximumFormatDoubleLength - 5 - s_newLineLength); @@ -321,7 +321,7 @@ private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, double private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, double value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - JsonConstants.MaximumFormatDoubleLength - 5 - s_newLineLength); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Float.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Float.cs index 3832dd4c0cebe5..7f85eedf18acb1 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Float.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Float.cs @@ -275,7 +275,7 @@ private void WriteNumberMinimized(ReadOnlySpan escapedPropertyName, float private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, float value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - JsonConstants.MaximumFormatSingleLength - 5 - s_newLineLength); @@ -321,7 +321,7 @@ private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, float v private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, float value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - JsonConstants.MaximumFormatSingleLength - 5 - s_newLineLength); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Guid.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Guid.cs index 07ffde8f49278e..5d918437b1cd3e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Guid.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Guid.cs @@ -279,7 +279,7 @@ private void WriteStringMinimized(ReadOnlySpan escapedPropertyName, Guid v private void WriteStringIndented(ReadOnlySpan escapedPropertyName, Guid value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - JsonConstants.MaximumFormatGuidLength - 7 - s_newLineLength); @@ -329,7 +329,7 @@ private void WriteStringIndented(ReadOnlySpan escapedPropertyName, Guid va private void WriteStringIndented(ReadOnlySpan escapedPropertyName, Guid value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - JsonConstants.MaximumFormatGuidLength - 7 - s_newLineLength); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Helpers.cs index 3bcddfd82a2d3e..3ea7aeaa1aaed4 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Helpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Helpers.cs @@ -13,22 +13,22 @@ public sealed partial class Utf8JsonWriter [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ValidatePropertyNameAndDepth(ReadOnlySpan propertyName) { - if (propertyName.Length > JsonConstants.MaxCharacterTokenSize || CurrentDepth >= JsonConstants.MaxWriterDepth) - ThrowHelper.ThrowInvalidOperationOrArgumentException(propertyName, _currentDepth); + if (propertyName.Length > JsonConstants.MaxCharacterTokenSize || CurrentDepth >= _options.MaxDepth) + ThrowHelper.ThrowInvalidOperationOrArgumentException(propertyName, _currentDepth, _options.MaxDepth); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ValidatePropertyNameAndDepth(ReadOnlySpan utf8PropertyName) { - if (utf8PropertyName.Length > JsonConstants.MaxUnescapedTokenSize || CurrentDepth >= JsonConstants.MaxWriterDepth) - ThrowHelper.ThrowInvalidOperationOrArgumentException(utf8PropertyName, _currentDepth); + if (utf8PropertyName.Length > JsonConstants.MaxUnescapedTokenSize || CurrentDepth >= _options.MaxDepth) + ThrowHelper.ThrowInvalidOperationOrArgumentException(utf8PropertyName, _currentDepth, _options.MaxDepth); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ValidateDepth() { - if (CurrentDepth >= JsonConstants.MaxWriterDepth) - ThrowHelper.ThrowInvalidOperationException(_currentDepth); + if (CurrentDepth >= _options.MaxDepth) + ThrowHelper.ThrowInvalidOperationException(_currentDepth, _options.MaxDepth); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -39,7 +39,7 @@ private void ValidateWritingProperty() if (!_inObject || _tokenType == JsonTokenType.PropertyName) { Debug.Assert(_tokenType != JsonTokenType.StartObject); - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotWritePropertyWithinArray, currentDepth: default, token: default, _tokenType); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotWritePropertyWithinArray, currentDepth: default, maxDepth: _options.MaxDepth, token: default, _tokenType); } } } @@ -52,7 +52,7 @@ private void ValidateWritingProperty(byte token) if (!_inObject || _tokenType == JsonTokenType.PropertyName) { Debug.Assert(_tokenType != JsonTokenType.StartObject); - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotWritePropertyWithinArray, currentDepth: default, token: default, _tokenType); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotWritePropertyWithinArray, currentDepth: default, maxDepth: _options.MaxDepth, token: default, _tokenType); } UpdateBitStackOnStart(token); } @@ -89,7 +89,7 @@ private void WritePropertyNameMinimized(ReadOnlySpan escapedPropertyName, private void WritePropertyNameIndented(ReadOnlySpan escapedPropertyName, byte token) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - 6 - s_newLineLength); @@ -161,7 +161,7 @@ private void WritePropertyNameMinimized(ReadOnlySpan escapedPropertyName, private void WritePropertyNameIndented(ReadOnlySpan escapedPropertyName, byte token) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - 6 - s_newLineLength); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Literal.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Literal.cs index 9ab0b654cf97da..5525f6fd80c541 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Literal.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Literal.cs @@ -415,7 +415,7 @@ private void WriteLiteralSection(ReadOnlySpan escapedPropertyNameSection, private void WriteLiteralIndented(ReadOnlySpan escapedPropertyName, ReadOnlySpan value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(value.Length <= JsonConstants.MaxUnescapedTokenSize); Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - value.Length - 5 - s_newLineLength); @@ -461,7 +461,7 @@ private void WriteLiteralIndented(ReadOnlySpan escapedPropertyName, ReadOn private void WriteLiteralIndented(ReadOnlySpan escapedPropertyName, ReadOnlySpan value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(value.Length <= JsonConstants.MaxUnescapedTokenSize); Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - value.Length - 5 - s_newLineLength); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.SignedNumber.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.SignedNumber.cs index 05d45181da0358..e2da3682635cd1 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.SignedNumber.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.SignedNumber.cs @@ -341,7 +341,7 @@ private void WriteNumberMinimized(ReadOnlySpan escapedPropertyName, long v private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, long value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - JsonConstants.MaximumFormatInt64Length - 5 - s_newLineLength); @@ -387,7 +387,7 @@ private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, long va private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, long value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - JsonConstants.MaximumFormatInt64Length - 5 - s_newLineLength); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.String.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.String.cs index 38c64b4a400f3b..543afe1c89df71 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.String.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.String.cs @@ -183,7 +183,7 @@ private void WriteStringMinimizedPropertyName(ReadOnlySpan escapedProperty private void WriteStringIndentedPropertyName(ReadOnlySpan escapedPropertyName) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length <= JsonConstants.MaxEscapedTokenSize); Debug.Assert(escapedPropertyName.Length < (int.MaxValue - 5 - indent - s_newLineLength) / JsonConstants.MaxExpansionFactorWhileTranscoding); @@ -374,7 +374,7 @@ private void WriteStringPropertyNameSection(ReadOnlySpan escapedPropertyNa private void WriteStringIndentedPropertyName(ReadOnlySpan escapedPropertyName) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length <= JsonConstants.MaxEscapedTokenSize); Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - 5 - s_newLineLength); @@ -1530,7 +1530,7 @@ private void WriteStringMinimized(ReadOnlySpan escapedPropertyName, ReadOn private void WriteStringIndented(ReadOnlySpan escapedPropertyName, ReadOnlySpan escapedValue) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedValue.Length <= JsonConstants.MaxEscapedTokenSize); Debug.Assert(escapedPropertyName.Length < ((int.MaxValue - 7 - indent - s_newLineLength) / JsonConstants.MaxExpansionFactorWhileTranscoding) - escapedValue.Length); @@ -1580,7 +1580,7 @@ private void WriteStringIndented(ReadOnlySpan escapedPropertyName, ReadOnl private void WriteStringIndented(ReadOnlySpan escapedPropertyName, ReadOnlySpan escapedValue) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedValue.Length <= JsonConstants.MaxEscapedTokenSize); Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - escapedValue.Length - 7 - s_newLineLength); @@ -1631,7 +1631,7 @@ private void WriteStringIndented(ReadOnlySpan escapedPropertyName, ReadOnl private void WriteStringIndented(ReadOnlySpan escapedPropertyName, ReadOnlySpan escapedValue) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedValue.Length <= JsonConstants.MaxEscapedTokenSize); Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - escapedValue.Length - 7 - indent - s_newLineLength); @@ -1682,7 +1682,7 @@ private void WriteStringIndented(ReadOnlySpan escapedPropertyName, ReadOnl private void WriteStringIndented(ReadOnlySpan escapedPropertyName, ReadOnlySpan escapedValue) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedValue.Length <= JsonConstants.MaxEscapedTokenSize); Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - escapedValue.Length - 7 - indent - s_newLineLength); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.UnsignedNumber.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.UnsignedNumber.cs index b7602d4f481c05..0ca71b89c37d5e 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.UnsignedNumber.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.UnsignedNumber.cs @@ -350,7 +350,7 @@ private void WriteNumberMinimized(ReadOnlySpan escapedPropertyName, ulong private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, ulong value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - JsonConstants.MaximumFormatUInt64Length - 5 - s_newLineLength); @@ -396,7 +396,7 @@ private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, ulong v private void WriteNumberIndented(ReadOnlySpan escapedPropertyName, ulong value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - JsonConstants.MaximumFormatUInt64Length - 5 - s_newLineLength); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Bytes.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Bytes.cs index 07d8077c16c193..fb6376fe4d9691 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Bytes.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Bytes.cs @@ -81,7 +81,7 @@ private void WriteBase64Minimized(ReadOnlySpan bytes) private void WriteBase64Indented(ReadOnlySpan bytes) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); int encodingLength = Base64.GetMaxEncodedToUtf8Length(bytes.Length); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Comment.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Comment.cs index a1ff7d659d7a58..d7696e1fbb09a9 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Comment.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Comment.cs @@ -92,7 +92,7 @@ private void WriteCommentMinimized(ReadOnlySpan value) private void WriteCommentIndented(ReadOnlySpan value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(value.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - 4 - s_newLineLength); @@ -187,7 +187,7 @@ private void WriteCommentMinimized(ReadOnlySpan utf8Value) private void WriteCommentIndented(ReadOnlySpan utf8Value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(utf8Value.Length < int.MaxValue - indent - 4 - s_newLineLength); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.DateTime.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.DateTime.cs index 0d5687c2be4a00..f87753be0ae37d 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.DateTime.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.DateTime.cs @@ -66,7 +66,7 @@ private void WriteStringValueMinimized(DateTime value) private void WriteStringValueIndented(DateTime value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); // 2 quotes, and optionally, 1 list separator and 1-2 bytes for new line int maxRequired = indent + JsonConstants.MaximumFormatDateTimeOffsetLength + 3 + s_newLineLength; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.DateTimeOffset.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.DateTimeOffset.cs index b92b799217954b..2608606375b396 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.DateTimeOffset.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.DateTimeOffset.cs @@ -67,7 +67,7 @@ private void WriteStringValueMinimized(DateTimeOffset value) private void WriteStringValueIndented(DateTimeOffset value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); // 2 quotes, and optionally, 1 list separator and 1-2 bytes for new line int maxRequired = indent + JsonConstants.MaximumFormatDateTimeOffsetLength + 3 + s_newLineLength; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Decimal.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Decimal.cs index a30af2e6546ad1..2462b667fe8ba1 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Decimal.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Decimal.cs @@ -63,7 +63,7 @@ private void WriteNumberValueMinimized(decimal value) private void WriteNumberValueIndented(decimal value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); int maxRequired = indent + JsonConstants.MaximumFormatDecimalLength + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Double.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Double.cs index f8a46a31468ca9..a0989d1c323b98 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Double.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Double.cs @@ -67,7 +67,7 @@ private void WriteNumberValueMinimized(double value) private void WriteNumberValueIndented(double value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); int maxRequired = indent + JsonConstants.MaximumFormatDoubleLength + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Float.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Float.cs index 2f046d872f6972..e12bab4ac01859 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Float.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Float.cs @@ -67,7 +67,7 @@ private void WriteNumberValueMinimized(float value) private void WriteNumberValueIndented(float value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); int maxRequired = indent + JsonConstants.MaximumFormatSingleLength + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.FormattedNumber.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.FormattedNumber.cs index 5dfb4e9e8ff484..f9005243fac4c6 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.FormattedNumber.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.FormattedNumber.cs @@ -67,7 +67,7 @@ private void WriteNumberValueMinimized(ReadOnlySpan utf8Value) private void WriteNumberValueIndented(ReadOnlySpan utf8Value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(utf8Value.Length < int.MaxValue - indent - 1 - s_newLineLength); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Guid.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Guid.cs index e9884f744a5812..227af26a3d2c00 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Guid.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Guid.cs @@ -67,7 +67,7 @@ private void WriteStringValueMinimized(Guid value) private void WriteStringValueIndented(Guid value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); // 2 quotes, and optionally, 1 list separator and 1-2 bytes for new line int maxRequired = indent + JsonConstants.MaximumFormatGuidLength + 3 + s_newLineLength; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Helpers.cs index d95817a638e85f..731e62867f2cbe 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Helpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Helpers.cs @@ -19,7 +19,7 @@ private void ValidateWritingValue() if (_tokenType != JsonTokenType.PropertyName) { Debug.Assert(_tokenType != JsonTokenType.None && _tokenType != JsonTokenType.StartArray); - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotWriteValueWithinObject, currentDepth: default, token: default, _tokenType); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotWriteValueWithinObject, currentDepth: default, maxDepth: _options.MaxDepth, token: default, _tokenType); } } else @@ -29,7 +29,7 @@ private void ValidateWritingValue() // It is more likely for CurrentDepth to not equal 0 when writing valid JSON, so check that first to rely on short-circuiting and return quickly. if (CurrentDepth == 0 && _tokenType != JsonTokenType.None) { - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotWriteValueAfterPrimitiveOrClose, currentDepth: default, token: default, _tokenType); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotWriteValueAfterPrimitiveOrClose, currentDepth: default, maxDepth: _options.MaxDepth, token: default, _tokenType); } } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Literal.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Literal.cs index c5beb15ae3f1db..b4bcc5b480246c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Literal.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Literal.cs @@ -86,7 +86,7 @@ private void WriteLiteralMinimized(ReadOnlySpan utf8Value) private void WriteLiteralIndented(ReadOnlySpan utf8Value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(utf8Value.Length <= 5); int maxRequired = indent + utf8Value.Length + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.SignedNumber.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.SignedNumber.cs index 2d6120a6a87680..dd47dfe230550f 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.SignedNumber.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.SignedNumber.cs @@ -76,7 +76,7 @@ private void WriteNumberValueMinimized(long value) private void WriteNumberValueIndented(long value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); int maxRequired = indent + JsonConstants.MaximumFormatInt64Length + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.String.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.String.cs index b4be2dfe8b3bb3..6513d001930b77 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.String.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.String.cs @@ -143,7 +143,7 @@ private void WriteStringMinimized(ReadOnlySpan escapedValue) private void WriteStringIndented(ReadOnlySpan escapedValue) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedValue.Length < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileTranscoding) - indent - 3 - s_newLineLength); @@ -290,7 +290,7 @@ private void WriteStringMinimized(ReadOnlySpan escapedValue) private void WriteStringIndented(ReadOnlySpan escapedValue) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(escapedValue.Length < int.MaxValue - indent - 3 - s_newLineLength); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.UnsignedNumber.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.UnsignedNumber.cs index d3cf96947db468..a348c125c8172c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.UnsignedNumber.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.UnsignedNumber.cs @@ -78,7 +78,7 @@ private void WriteNumberValueMinimized(ulong value) private void WriteNumberValueIndented(ulong value) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); int maxRequired = indent + JsonConstants.MaximumFormatUInt64Length + 1 + s_newLineLength; // Optionally, 1 list separator and 1-2 bytes for new line diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.cs index 17e08065a39f91..084b7c65c6350c 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.cs @@ -103,6 +103,11 @@ public Utf8JsonWriter(IBufferWriter bufferWriter, JsonWriterOptions option { _output = bufferWriter ?? throw new ArgumentNullException(nameof(bufferWriter)); _options = options; + + if (_options.MaxDepth == 0) + { + _options.MaxDepth = JsonWriterOptions.DefaultMaxDepth; // If max depth is not set, revert to the default depth. + } } /// @@ -124,6 +129,12 @@ public Utf8JsonWriter(Stream utf8Json, JsonWriterOptions options = default) _stream = utf8Json; _options = options; + + if (_options.MaxDepth == 0) + { + _options.MaxDepth = JsonWriterOptions.DefaultMaxDepth; // If max depth is not set, revert to the default depth. + } + _arrayBufferWriter = new ArrayBufferWriter(); } @@ -426,8 +437,8 @@ public void WriteStartObject() private void WriteStart(byte token) { - if (CurrentDepth >= JsonConstants.MaxWriterDepth) - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.DepthTooLarge, _currentDepth, token: default, tokenType: default); + if (CurrentDepth >= _options.MaxDepth) + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.DepthTooLarge, _currentDepth, _options.MaxDepth, token: default, tokenType: default); if (_options.IndentedOrNotSkipValidation) { @@ -486,7 +497,7 @@ private void ValidateStart() if (_tokenType != JsonTokenType.PropertyName) { Debug.Assert(_tokenType != JsonTokenType.None && _tokenType != JsonTokenType.StartArray); - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotStartObjectArrayWithoutProperty, currentDepth: default, token: default, _tokenType); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotStartObjectArrayWithoutProperty, currentDepth: default, maxDepth: _options.MaxDepth, token: default, _tokenType); } } else @@ -497,7 +508,7 @@ private void ValidateStart() // It is more likely for CurrentDepth to not equal 0 when writing valid JSON, so check that first to rely on short-circuiting and return quickly. if (CurrentDepth == 0 && _tokenType != JsonTokenType.None) { - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotStartObjectArrayAfterPrimitiveOrClose, currentDepth: default, token: default, _tokenType); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.CannotStartObjectArrayAfterPrimitiveOrClose, currentDepth: default, maxDepth: _options.MaxDepth, token: default, _tokenType); } } } @@ -505,7 +516,7 @@ private void ValidateStart() private void WriteStartIndented(byte token) { int indent = Indentation; - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); int minRequired = indent + 1; // 1 start token int maxRequired = minRequired + 3; // Optionally, 1 list separator and 1-2 bytes for new line @@ -898,14 +909,14 @@ private void WriteEndSlow(byte token) private void ValidateEnd(byte token) { if (_bitStack.CurrentDepth <= 0 || _tokenType == JsonTokenType.PropertyName) - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.MismatchedObjectArray, currentDepth: default, token, _tokenType); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.MismatchedObjectArray, currentDepth: default, maxDepth: _options.MaxDepth, token, _tokenType); if (token == JsonConstants.CloseBracket) { if (_inObject) { Debug.Assert(_tokenType != JsonTokenType.None); - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.MismatchedObjectArray, currentDepth: default, token, _tokenType); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.MismatchedObjectArray, currentDepth: default, maxDepth: _options.MaxDepth, token, _tokenType); } } else @@ -914,7 +925,7 @@ private void ValidateEnd(byte token) if (!_inObject) { - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.MismatchedObjectArray, currentDepth: default, token, _tokenType); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.MismatchedObjectArray, currentDepth: default, maxDepth: _options.MaxDepth, token, _tokenType); } } @@ -940,7 +951,7 @@ private void WriteEndIndented(byte token) indent -= JsonConstants.SpacesPerIndent; } - Debug.Assert(indent <= 2 * JsonConstants.MaxWriterDepth); + Debug.Assert(indent <= 2 * _options.MaxDepth); Debug.Assert(_options.SkipValidation || _tokenType != JsonTokenType.None); int maxRequired = indent + 3; // 1 end token, 1-2 bytes for new line diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonWriterOptionsTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonWriterOptionsTests.cs index 2674a2e992c871..41a6c5c203a917 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonWriterOptionsTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonWriterOptionsTests.cs @@ -15,7 +15,8 @@ public static void JsonWriterOptionsDefaultCtor() var expectedOption = new JsonWriterOptions { Indented = false, - SkipValidation = false + SkipValidation = false, + MaxDepth = 0, }; Assert.Equal(expectedOption, options); } @@ -28,28 +29,40 @@ public static void JsonWriterOptionsCtor() var expectedOption = new JsonWriterOptions { Indented = false, - SkipValidation = false + SkipValidation = false, + MaxDepth = 0, }; Assert.Equal(expectedOption, options); } [Theory] - [InlineData(true, true)] - [InlineData(true, false)] - [InlineData(false, true)] - [InlineData(false, false)] - public static void JsonWriterOptions(bool indented, bool skipValidation) + [InlineData(true, true, 0)] + [InlineData(true, false, 1)] + [InlineData(false, true, 1024)] + [InlineData(false, false, 1024 * 1024)] + public static void JsonWriterOptions(bool indented, bool skipValidation, int maxDepth) { var options = new JsonWriterOptions(); options.Indented = indented; options.SkipValidation = skipValidation; + options.MaxDepth = maxDepth; var expectedOption = new JsonWriterOptions { Indented = indented, - SkipValidation = skipValidation + SkipValidation = skipValidation, + MaxDepth = maxDepth, }; Assert.Equal(expectedOption, options); } + + [Theory] + [InlineData(-1)] + [InlineData(-100)] + public static void JsonWriterOptions_MaxDepth_InvalidParameters(int maxDepth) + { + var options = new JsonWriterOptions(); + Assert.Throws(() => options.MaxDepth = maxDepth); + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReadValueTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReadValueTests.cs index c29c4010391614..e7eb6f5b314a4d 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReadValueTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/ReadValueTests.cs @@ -673,7 +673,7 @@ public static void TooLittleJsonForIntArray(string json) // From https://github.com/dotnet/runtime/issues/882 [Fact] - public static void OptionsFollowToConverter() + public static void OptionsFlowToConverter() { var builder = new StringBuilder(); builder.Append("{\"type\": \"array\", \"array\": "); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/WriteValueTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/WriteValueTests.cs index ceb0e33fe8e1be..66b89f1c151f80 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/WriteValueTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/WriteValueTests.cs @@ -393,5 +393,71 @@ public static void SerializeExceedMaximumBufferSize() Assert.Throws(() => JsonSerializer.Serialize(temp, typeof(CustomClassToExceedMaxBufferSize))); } + + [Theory] + [InlineData(0)] + [InlineData(10)] + [InlineData(500)] + public static void MaxDepthFlowsToConverter(int maxDepth) + { + var converter = new InstrumentedConverter(); + var options = new JsonSerializerOptions { Converters = { converter }, MaxDepth = maxDepth }; + int effectiveMaxDepth = maxDepth == 0 ? 64 : maxDepth; + + JsonSerializer.Serialize(value: 42, options); + + Assert.Equal(effectiveMaxDepth, converter.WriterOptions.MaxDepth); + } + + private class InstrumentedConverter : JsonConverter + { + public JsonWriterOptions WriterOptions { get; private set; } + + public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException(); + public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options) + { + WriterOptions = writer.Options; + writer.WriteNumberValue(value); + } + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + public static void CustomMaxDepth_DepthWithinLimit_Succeeds(int maxDepth) + { + var options = new JsonSerializerOptions { MaxDepth = maxDepth }; + int effectiveMaxDepth = maxDepth == 0 ? 64 : maxDepth; + + Peano? value = Peano.CreateFromNumber(effectiveMaxDepth); + JsonSerializer.Serialize(value, options); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(10)] + [InlineData(100)] + public static void CustomMaxDepth_DepthExceedsLimit_Fails(int maxDepth) + { + var options = new JsonSerializerOptions { MaxDepth = maxDepth }; + int effectiveMaxDepth = maxDepth == 0 ? 64 : maxDepth; + + Peano value = Peano.CreateFromNumber(effectiveMaxDepth + 1); + JsonException exn = Assert.Throws(() => JsonSerializer.Serialize(value, options)); + Assert.Contains("A possible object cycle was detected", exn.Message); + } + + public class Peano + { + public Peano? Successor { get; init; } + + public static Peano? CreateFromNumber(int value) + { + return value == 0 ? null : new Peano { Successor = CreateFromNumber(value - 1) }; + } + } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.cs index f18f23a64f3c6e..01701471c0f14e 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.cs @@ -2911,6 +2911,59 @@ public void WritingTooDeepProperty(bool formatted, bool skipValidation) } } + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(10)] + [InlineData(2048)] + [InlineData(1024 * 1024)] + public static void CustomMaxDepth_DepthWithinLimit_ShouldSucceed(int maxDepth) + { + var options = new JsonWriterOptions { MaxDepth = maxDepth }; + int effectiveMaxDepth = maxDepth == 0 ? 1000 : maxDepth; + + var output = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(output, options); + + for (int i = 0; i < effectiveMaxDepth; i++) + { + writer.WriteStartArray(); + } + + Assert.Equal(effectiveMaxDepth, writer.CurrentDepth); + + for (int i = 0; i < effectiveMaxDepth; i++) + { + writer.WriteEndArray(); + } + + Assert.Equal(0, writer.CurrentDepth); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(10)] + [InlineData(2048)] + [InlineData(1024 * 1024)] + public static void CustomMaxDepth_DepthExceedingLimit_ShouldFail(int maxDepth) + { + var options = new JsonWriterOptions { MaxDepth = maxDepth }; + int effectiveMaxDepth = maxDepth == 0 ? 1000 : maxDepth; + + var output = new ArrayBufferWriter(); + using var writer = new Utf8JsonWriter(output, options); + + for (int i = 0; i < effectiveMaxDepth; i++) + { + writer.WriteStartArray(); + } + + Assert.Equal(effectiveMaxDepth, writer.CurrentDepth); + + Assert.Throws(() => writer.WriteStartArray()); + } + // NOTE: WritingTooLargeProperty test is constrained to run on Windows and MacOSX because it causes // problems on Linux due to the way deferred memory allocation works. On Linux, the allocation can // succeed even if there is not enough memory but then the test may get killed by the OOM killer at the