From 0eb4f700ada167e4a084c45700b7feb5955b1546 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Mon, 15 Nov 2021 17:24:40 +0000 Subject: [PATCH 1/3] Add a JsonWriterOptions.MaxDepth property --- .../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 1cde69c8ace1a..f6b25cdcb23d4 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 f004e8d5d8347..bed577622f5fa 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 79c72342f78ef..4ddf4db675d42 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 092588cee842a..2721f685ae4dc 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 1c60e5939fa46..e71668b854323 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 55d8aaa515dee..7ea9b37514cba 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 e41427cdcdd8c..77b8975332cd9 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 313496df38c9b..6b1013d9868cc 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 ab98e246fab68..ef2e614549e78 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 1fb6832ce6ad3..658267052137a 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 3832dd4c0cebe..7f85eedf18acb 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 07ffde8f49278..5d918437b1cd3 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 3bcddfd82a2d3..3ea7aeaa1aaed 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 9ab0b654cf97d..5525f6fd80c54 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 05d45181da035..e2da3682635cd 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 38c64b4a400f3..543afe1c89df7 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 b7602d4f481c0..0ca71b89c37d5 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 07d8077c16c19..fb6376fe4d969 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 a1ff7d659d7a5..d7696e1fbb09a 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 0d5687c2be4a0..f87753be0ae37 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 b92b799217954..2608606375b39 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 a30af2e6546ad..2462b667fe8ba 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 f8a46a31468ca..a0989d1c323b9 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 2f046d872f697..e12bab4ac0185 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 5dfb4e9e8ff48..f9005243fac4c 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 e9884f744a581..227af26a3d2c0 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 d95817a638e85..731e62867f2cb 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 c5beb15ae3f1d..b4bcc5b480246 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 2d6120a6a8768..dd47dfe230550 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 b4be2dfe8b3bb..6513d001930b7 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 d3cf96947db46..a348c125c8172 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 17e08065a39f9..084b7c65c6350 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 2674a2e992c87..41a6c5c203a91 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 c29c401039161..e7eb6f5b314a4 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 ceb0e33fe8e1b..66b89f1c151f8 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 f18f23a64f3c6..01701471c0f14 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 From 0e430928c8d84288e0b2cb2e1d1726087f905ae8 Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Tue, 16 Nov 2021 20:15:56 +0000 Subject: [PATCH 2/3] remove depth checks from the converter layer --- .../System.Text.Json/src/Resources/Strings.resx | 5 +---- .../System/Text/Json/Serialization/JsonConverterOfT.cs | 10 ---------- .../src/System/Text/Json/ThrowHelper.Serialization.cs | 7 ------- .../Serialization/CyclicTests.cs | 3 ++- .../Serialization/WriteValueTests.cs | 3 ++- 5 files changed, 5 insertions(+), 23 deletions(-) diff --git a/src/libraries/System.Text.Json/src/Resources/Strings.resx b/src/libraries/System.Text.Json/src/Resources/Strings.resx index f76179e2d96be..356ef5b856c2f 100644 --- a/src/libraries/System.Text.Json/src/Resources/Strings.resx +++ b/src/libraries/System.Text.Json/src/Resources/Strings.resx @@ -423,9 +423,6 @@ Either the JSON value is not in a supported format, or is out of bounds for a UInt16. - - A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of {0}. Consider using ReferenceHandler.Preserve on JsonSerializerOptions to support cycles. - Invalid leading zero before '{0}'. @@ -620,4 +617,4 @@ 'JsonSerializerContext' '{0}' did not provide constructor parameter metadata for type '{1}'. - \ No newline at end of file + diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs index 3deefaf77a6ed..4ba41c2d10984 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs @@ -320,11 +320,6 @@ internal override sealed bool TryReadAsObject(ref Utf8JsonReader reader, JsonSer internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions options, ref WriteStack state) { - if (writer.CurrentDepth >= options.EffectiveMaxDepth) - { - ThrowHelper.ThrowJsonException_SerializerCycleDetected(options.EffectiveMaxDepth); - } - if (default(T) is null && !HandleNullOnWrite && IsNull(value)) { // We do not pass null values to converters unless HandleNullOnWrite is true. Null values for properties were @@ -510,11 +505,6 @@ internal bool TryWriteDataExtensionProperty(Utf8JsonWriter writer, T value, Json return TryWrite(writer, value, options, ref state); } - if (writer.CurrentDepth >= options.EffectiveMaxDepth) - { - ThrowHelper.ThrowJsonException_SerializerCycleDetected(options.EffectiveMaxDepth); - } - bool isContinuation = state.IsContinuation; bool success; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs index a75f5eaa48406..0d16943c08558 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs @@ -83,13 +83,6 @@ public static void ThrowJsonException_SerializationConverterWrite(JsonConverter? throw new JsonException(SR.Format(SR.SerializationConverterWrite, converter)) { AppendPathInformation = true }; } - [DoesNotReturn] - [MethodImpl(MethodImplOptions.NoInlining)] - public static void ThrowJsonException_SerializerCycleDetected(int maxDepth) - { - throw new JsonException(SR.Format(SR.SerializerCycleDetected, maxDepth)) { AppendPathInformation = true }; - } - [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowJsonException(string? message = null) diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CyclicTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CyclicTests.cs index 20ff0837cbea2..323c900adea7c 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CyclicTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CyclicTests.cs @@ -52,12 +52,13 @@ public static void WriteCyclicFail(int objectHierarchyDepth, int maxDepth, int e var options = new JsonSerializerOptions(); options.MaxDepth = maxDepth; JsonException ex = Assert.Throws(() => JsonSerializer.Serialize(rootObj, options)); + InvalidOperationException innerEx = Assert.IsType(ex.InnerException); // Exception should contain the path and MaxDepth. // Since the last Parent property is null, the serializer moves onto the Children property. string expectedPath = "$" + string.Concat(Enumerable.Repeat(".Parent", expectedPathDepth)); Assert.Contains(expectedPath, ex.Path); - Assert.Contains(effectiveMaxDepth.ToString(), ex.Message); + Assert.Contains(effectiveMaxDepth.ToString(), innerEx.Message); } private static TestClassWithCycle CreateObjectHierarchy(int i, int max, TestClassWithCycle previous) 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 66b89f1c151f8..704fa18883081 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 @@ -447,7 +447,8 @@ public static void CustomMaxDepth_DepthExceedsLimit_Fails(int 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); + InvalidOperationException innerExn = Assert.IsType(exn.InnerException); + Assert.Contains("is equal to or larger than the maximum allowed depth", innerExn.Message); } public class Peano From 7a1c5c8c6cd666eb0ff3727a3389592f540e7fca Mon Sep 17 00:00:00 2001 From: Eirik Tsarpalis Date: Mon, 22 Nov 2021 15:23:17 +0000 Subject: [PATCH 3/3] Revert "remove depth checks from the converter layer" This reverts commit 0e430928c8d84288e0b2cb2e1d1726087f905ae8. --- .../System.Text.Json/src/Resources/Strings.resx | 5 ++++- .../System/Text/Json/Serialization/JsonConverterOfT.cs | 10 ++++++++++ .../src/System/Text/Json/ThrowHelper.Serialization.cs | 7 +++++++ .../Serialization/CyclicTests.cs | 3 +-- .../Serialization/WriteValueTests.cs | 3 +-- 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Text.Json/src/Resources/Strings.resx b/src/libraries/System.Text.Json/src/Resources/Strings.resx index 356ef5b856c2f..f76179e2d96be 100644 --- a/src/libraries/System.Text.Json/src/Resources/Strings.resx +++ b/src/libraries/System.Text.Json/src/Resources/Strings.resx @@ -423,6 +423,9 @@ Either the JSON value is not in a supported format, or is out of bounds for a UInt16. + + A possible object cycle was detected. This can either be due to a cycle or if the object depth is larger than the maximum allowed depth of {0}. Consider using ReferenceHandler.Preserve on JsonSerializerOptions to support cycles. + Invalid leading zero before '{0}'. @@ -617,4 +620,4 @@ 'JsonSerializerContext' '{0}' did not provide constructor parameter metadata for type '{1}'. - + \ No newline at end of file diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs index 4ba41c2d10984..3deefaf77a6ed 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs @@ -320,6 +320,11 @@ internal override sealed bool TryReadAsObject(ref Utf8JsonReader reader, JsonSer internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions options, ref WriteStack state) { + if (writer.CurrentDepth >= options.EffectiveMaxDepth) + { + ThrowHelper.ThrowJsonException_SerializerCycleDetected(options.EffectiveMaxDepth); + } + if (default(T) is null && !HandleNullOnWrite && IsNull(value)) { // We do not pass null values to converters unless HandleNullOnWrite is true. Null values for properties were @@ -505,6 +510,11 @@ internal bool TryWriteDataExtensionProperty(Utf8JsonWriter writer, T value, Json return TryWrite(writer, value, options, ref state); } + if (writer.CurrentDepth >= options.EffectiveMaxDepth) + { + ThrowHelper.ThrowJsonException_SerializerCycleDetected(options.EffectiveMaxDepth); + } + bool isContinuation = state.IsContinuation; bool success; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs index 0d16943c08558..a75f5eaa48406 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs @@ -83,6 +83,13 @@ public static void ThrowJsonException_SerializationConverterWrite(JsonConverter? throw new JsonException(SR.Format(SR.SerializationConverterWrite, converter)) { AppendPathInformation = true }; } + [DoesNotReturn] + [MethodImpl(MethodImplOptions.NoInlining)] + public static void ThrowJsonException_SerializerCycleDetected(int maxDepth) + { + throw new JsonException(SR.Format(SR.SerializerCycleDetected, maxDepth)) { AppendPathInformation = true }; + } + [DoesNotReturn] [MethodImpl(MethodImplOptions.NoInlining)] public static void ThrowJsonException(string? message = null) diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CyclicTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CyclicTests.cs index 323c900adea7c..20ff0837cbea2 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CyclicTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CyclicTests.cs @@ -52,13 +52,12 @@ public static void WriteCyclicFail(int objectHierarchyDepth, int maxDepth, int e var options = new JsonSerializerOptions(); options.MaxDepth = maxDepth; JsonException ex = Assert.Throws(() => JsonSerializer.Serialize(rootObj, options)); - InvalidOperationException innerEx = Assert.IsType(ex.InnerException); // Exception should contain the path and MaxDepth. // Since the last Parent property is null, the serializer moves onto the Children property. string expectedPath = "$" + string.Concat(Enumerable.Repeat(".Parent", expectedPathDepth)); Assert.Contains(expectedPath, ex.Path); - Assert.Contains(effectiveMaxDepth.ToString(), innerEx.Message); + Assert.Contains(effectiveMaxDepth.ToString(), ex.Message); } private static TestClassWithCycle CreateObjectHierarchy(int i, int max, TestClassWithCycle previous) 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 704fa18883081..66b89f1c151f8 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 @@ -447,8 +447,7 @@ public static void CustomMaxDepth_DepthExceedsLimit_Fails(int maxDepth) Peano value = Peano.CreateFromNumber(effectiveMaxDepth + 1); JsonException exn = Assert.Throws(() => JsonSerializer.Serialize(value, options)); - InvalidOperationException innerExn = Assert.IsType(exn.InnerException); - Assert.Contains("is equal to or larger than the maximum allowed depth", innerExn.Message); + Assert.Contains("A possible object cycle was detected", exn.Message); } public class Peano