Update CurrentDepth semantics to match current token depth, not reader state (dotnet...
authorAhson Khan <ahkha@microsoft.com>
Tue, 19 Mar 2019 01:28:38 +0000 (18:28 -0700)
committerGitHub <noreply@github.com>
Tue, 19 Mar 2019 01:28:38 +0000 (18:28 -0700)
* Update CurrentDepth semantics to match current token depth, not reader
state

* Remove use of CurrentDepth from exception message and add tests.

* Use bitstack.CurrentDepth and update exception message.

Commit migrated from https://github.com/dotnet/corefx/commit/44f54db21b45fb1b8aa4490833e2d64700801849

src/libraries/System.Text.Json/src/Resources/Strings.resx
src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.MultiSegment.cs
src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.cs
src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.cs
src/libraries/System.Text.Json/tests/Utf8JsonReaderTests.cs

index 206c18f..0ba9a73 100644 (file)
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
   <data name="ArrayDepthTooLarge" xml:space="preserve">
-    <value>CurrentDepth ({0}) is larger than the maximum configured depth of {1}. Cannot read next JSON array.</value>
+    <value>The maximum configured depth of {0} has been exceeded. Cannot read next JSON array.</value>
   </data>
   <data name="CallFlushToAvoidDataLoss" xml:space="preserve">
     <value>The JSON writer needs to be flushed before getting the current state. There are {0} bytes that have not been committed to the output.</value>
     <value>'{0}' is invalid without a matching open.</value>
   </data>
   <data name="ObjectDepthTooLarge" xml:space="preserve">
-    <value>CurrentDepth ({0}) is larger than the maximum configured depth of {1}. Cannot read next JSON object.</value>
+    <value>The maximum configured depth of {0} has been exceeded. Cannot read next JSON object.</value>
   </data>
   <data name="PropertyNameTooLarge" xml:space="preserve">
     <value>The JSON property name of length {0} is too large and not supported by the JSON writer.</value>
     <value>The JSON value of length {0} is too large and not supported by the JSON writer.</value>
   </data>
   <data name="ZeroDepthAtEnd" xml:space="preserve">
-    <value>Expected CurrentDepth ({0}) to be zero at the end of the JSON payload. There is an open JSON object or array that should be closed.</value>
+    <value>Expected depth to be zero at the end of the JSON payload. There is an open JSON object or array that should be closed.</value>
   </data>
   <data name="DeserializeCannotBeNull" xml:space="preserve">
     <value>The JSON value from {0} cannot be null.</value>
index ae7f8cd..1997a9a 100644 (file)
@@ -1574,7 +1574,7 @@ namespace System.Text.Json
                 }
             }
 
-            if (CurrentDepth == 0)
+            if (_bitStack.CurrentDepth == 0)
             {
                 ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker);
             }
@@ -1681,7 +1681,7 @@ namespace System.Text.Json
                 first = _buffer[_consumed];
             }
 
-            if (CurrentDepth == 0 && _tokenType != JsonTokenType.None)
+            if (_bitStack.CurrentDepth == 0 && _tokenType != JsonTokenType.None)
             {
                 ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, first);
             }
@@ -1971,7 +1971,7 @@ namespace System.Text.Json
                 }
                 goto Done;
             }
-            else if (CurrentDepth == 0)
+            else if (_bitStack.CurrentDepth == 0)
             {
                 ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker);
             }
index 767c8be..c0f90a1 100644 (file)
@@ -72,7 +72,19 @@ namespace System.Text.Json
         /// Tracks the recursive depth of the nested objects / arrays within the JSON text
         /// processed so far. This provides the depth of the current token.
         /// </summary>
-        public int CurrentDepth => _bitStack.CurrentDepth;
+        public int CurrentDepth
+        {
+            get
+            {
+                int readerDepth = _bitStack.CurrentDepth;
+                if (TokenType == JsonTokenType.StartArray || TokenType == JsonTokenType.StartObject)
+                {
+                    Debug.Assert(readerDepth >= 1);
+                    readerDepth--;
+                }
+                return readerDepth;
+            }
+        }
 
         internal bool IsInArray => !_inObject;
 
@@ -496,7 +508,7 @@ namespace System.Text.Json
 
         private void StartObject()
         {
-            if (CurrentDepth >= _readerOptions.MaxDepth)
+            if (_bitStack.CurrentDepth >= _readerOptions.MaxDepth)
                 ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ObjectDepthTooLarge);
 
             _bitStack.PushTrue();
@@ -510,7 +522,7 @@ namespace System.Text.Json
 
         private void EndObject()
         {
-            if (!_inObject || CurrentDepth <= 0)
+            if (!_inObject || _bitStack.CurrentDepth <= 0)
                 ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.MismatchedObjectArray, JsonConstants.CloseBrace);
 
             _tokenType = JsonTokenType.EndObject;
@@ -521,7 +533,7 @@ namespace System.Text.Json
 
         private void StartArray()
         {
-            if (CurrentDepth >= _readerOptions.MaxDepth)
+            if (_bitStack.CurrentDepth >= _readerOptions.MaxDepth)
                 ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ArrayDepthTooLarge);
 
             _bitStack.PushFalse();
@@ -535,7 +547,7 @@ namespace System.Text.Json
 
         private void EndArray()
         {
-            if (_inObject || CurrentDepth <= 0)
+            if (_inObject || _bitStack.CurrentDepth <= 0)
                 ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.MismatchedObjectArray, JsonConstants.CloseBracket);
 
             _tokenType = JsonTokenType.EndArray;
@@ -657,7 +669,7 @@ namespace System.Text.Json
             {
                 if (_isNotPrimitive && IsLastSpan)
                 {
-                    if (CurrentDepth != 0)
+                    if (_bitStack.CurrentDepth != 0)
                     {
                         ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ZeroDepthAtEnd);
                     }
@@ -1474,7 +1486,7 @@ namespace System.Text.Json
                 }
             }
 
-            if (CurrentDepth == 0)
+            if (_bitStack.CurrentDepth == 0)
             {
                 ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker);
             }
@@ -1572,7 +1584,7 @@ namespace System.Text.Json
                 first = _buffer[_consumed];
             }
 
-            if (CurrentDepth == 0 && _tokenType != JsonTokenType.None)
+            if (_bitStack.CurrentDepth == 0 && _tokenType != JsonTokenType.None)
             {
                 ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, first);
             }
@@ -1847,7 +1859,7 @@ namespace System.Text.Json
                 }
                 goto Done;
             }
-            else if (CurrentDepth == 0)
+            else if (_bitStack.CurrentDepth == 0)
             {
                 ThrowHelper.ThrowJsonReaderException(ref this, ExceptionResource.ExpectedEndAfterSingleJson, marker);
             }
index 1d6cf8d..c09e300 100644 (file)
@@ -247,7 +247,7 @@ namespace System.Text.Json
             switch (resource)
             {
                 case ExceptionResource.ArrayDepthTooLarge:
-                    message = SR.Format(SR.ArrayDepthTooLarge, json.CurrentDepth + 1, json.CurrentState.Options.MaxDepth);
+                    message = SR.Format(SR.ArrayDepthTooLarge, json.CurrentState.Options.MaxDepth);
                     break;
                 case ExceptionResource.MismatchedObjectArray:
                     message = SR.Format(SR.MismatchedObjectArray, character);
@@ -295,7 +295,7 @@ namespace System.Text.Json
                     message = SR.Format(SR.InvalidEndOfJsonNonPrimitive, json.TokenType);
                     break;
                 case ExceptionResource.ObjectDepthTooLarge:
-                    message = SR.Format(SR.ObjectDepthTooLarge, json.CurrentDepth + 1, json.CurrentState.Options.MaxDepth);
+                    message = SR.Format(SR.ObjectDepthTooLarge, json.CurrentState.Options.MaxDepth);
                     break;
                 case ExceptionResource.ExpectedFalse:
                     message = SR.Format(SR.ExpectedFalse, characters);
@@ -319,7 +319,7 @@ namespace System.Text.Json
                     message = SR.EndOfCommentNotFound;
                     break;
                 case ExceptionResource.ZeroDepthAtEnd:
-                    message = SR.Format(SR.ZeroDepthAtEnd, json.CurrentDepth);
+                    message = SR.Format(SR.ZeroDepthAtEnd);
                     break;
                 case ExceptionResource.ExpectedJsonTokens:
                     message = SR.ExpectedJsonTokens;
index d02109d..02cd38b 100644 (file)
@@ -320,6 +320,81 @@ namespace System.Text.Json.Tests
             }
         }
 
+        [Fact]
+        public static void CurrentDepthArrayTest()
+        {
+            string jsonString =
+@"[
+    [
+        1,
+        2,
+        3
+    ]
+]";
+
+            byte[] dataUtf8 = Encoding.UTF8.GetBytes(jsonString);
+            var json = new Utf8JsonReader(dataUtf8, isFinalBlock: true, state: default);
+
+            Assert.Equal(0, json.CurrentDepth);
+            Assert.True(json.Read());
+            Assert.Equal(0, json.CurrentDepth);
+            Assert.True(json.Read());
+            Assert.Equal(1, json.CurrentDepth);
+            Assert.True(json.Read());
+            Assert.Equal(2, json.CurrentDepth);
+            Assert.True(json.Read());
+            Assert.Equal(2, json.CurrentDepth);
+            Assert.True(json.Read());
+            Assert.Equal(2, json.CurrentDepth);
+            Assert.True(json.Read());
+            Assert.Equal(1, json.CurrentDepth);
+            Assert.True(json.Read());
+            Assert.Equal(0, json.CurrentDepth);
+            Assert.False(json.Read());
+            Assert.Equal(0, json.CurrentDepth);
+        }
+
+        [Fact]
+        public static void CurrentDepthObjectTest()
+        {
+            string jsonString =
+@"{
+    ""array"": [
+        1,
+        2,
+        3,
+        {}
+    ]
+}";
+
+            byte[] dataUtf8 = Encoding.UTF8.GetBytes(jsonString);
+            var json = new Utf8JsonReader(dataUtf8, isFinalBlock: true, state: default);
+
+            Assert.Equal(0, json.CurrentDepth);
+            Assert.True(json.Read());
+            Assert.Equal(0, json.CurrentDepth);
+            Assert.True(json.Read());
+            Assert.Equal(1, json.CurrentDepth);
+            Assert.True(json.Read());
+            Assert.Equal(1, json.CurrentDepth);
+            Assert.True(json.Read());
+            Assert.Equal(2, json.CurrentDepth);
+            Assert.True(json.Read());
+            Assert.Equal(2, json.CurrentDepth);
+            Assert.True(json.Read());
+            Assert.Equal(2, json.CurrentDepth);
+            Assert.True(json.Read());
+            Assert.Equal(2, json.CurrentDepth);
+            Assert.True(json.Read());
+            Assert.Equal(2, json.CurrentDepth);
+            Assert.True(json.Read());
+            Assert.Equal(1, json.CurrentDepth);
+            Assert.True(json.Read());
+            Assert.Equal(0, json.CurrentDepth);
+            Assert.False(json.Read());
+            Assert.Equal(0, json.CurrentDepth);
+        }
+
         [Theory]
         // Pad depth by nested objects, but minimize the text
         [InlineData(1, true, true)]