diff --git a/moshi/src/main/java/com/squareup/moshi/JsonValueReader.java b/moshi/src/main/java/com/squareup/moshi/JsonValueReader.java index 57f5c76..063c35c 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonValueReader.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonValueReader.java @@ -49,7 +49,7 @@ final class JsonValueReader extends JsonReader { /** Sentinel object pushed on {@link #stack} when the reader is closed. */ private static final Object JSON_READER_CLOSED = new Object(); - private final Object[] stack = new Object[32]; + private Object[] stack = new Object[32]; JsonValueReader(Object root) { scopes[stackSize] = JsonScope.NONEMPTY_DOCUMENT; @@ -308,7 +308,13 @@ final class JsonValueReader extends JsonReader { private void push(Object newTop) { if (stackSize == stack.length) { - throw new JsonDataException("Nesting too deep at " + getPath()); + if (stackSize == 256) { + throw new JsonDataException("Nesting too deep at " + getPath()); + } + scopes = Arrays.copyOf(scopes, scopes.length * 2); + pathNames = Arrays.copyOf(pathNames, pathNames.length * 2); + pathIndices = Arrays.copyOf(pathIndices, pathIndices.length * 2); + stack = Arrays.copyOf(stack, stack.length * 2); } stack[stackSize++] = newTop; } diff --git a/moshi/src/test/java/com/squareup/moshi/JsonUtf8ReaderTest.java b/moshi/src/test/java/com/squareup/moshi/JsonUtf8ReaderTest.java index b9a6bf7..89f28a0 100644 --- a/moshi/src/test/java/com/squareup/moshi/JsonUtf8ReaderTest.java +++ b/moshi/src/test/java/com/squareup/moshi/JsonUtf8ReaderTest.java @@ -33,6 +33,7 @@ import static com.squareup.moshi.JsonReader.Token.NAME; import static com.squareup.moshi.JsonReader.Token.NULL; import static com.squareup.moshi.JsonReader.Token.NUMBER; import static com.squareup.moshi.JsonReader.Token.STRING; +import static com.squareup.moshi.TestUtil.MAX_DEPTH; import static com.squareup.moshi.TestUtil.newReader; import static com.squareup.moshi.TestUtil.repeat; import static org.assertj.core.api.Assertions.assertThat; @@ -1023,28 +1024,28 @@ public final class JsonUtf8ReaderTest { } @Test public void tooDeeplyNestedArrays() throws IOException { - JsonReader reader = newReader(repeat("[", 256) + repeat("]", 256)); - for (int i = 0; i < 255; i++) { + JsonReader reader = newReader(repeat("[", MAX_DEPTH + 1) + repeat("]", MAX_DEPTH + 1)); + for (int i = 0; i < MAX_DEPTH; i++) { reader.beginArray(); } try { reader.beginArray(); fail(); } catch (JsonDataException expected) { - assertThat(expected).hasMessage("Nesting too deep at $" + repeat("[0]", 255)); + assertThat(expected).hasMessage("Nesting too deep at $" + repeat("[0]", MAX_DEPTH)); } } @Test public void tooDeeplyNestedObjects() throws IOException { - // Build a JSON document structured like {"a":{"a":{"a":{"a":true}}}}, but 31 levels deep. + // Build a JSON document structured like {"a":{"a":{"a":{"a":true}}}}, but 255 levels deep. String array = "{\"a\":%s}"; String json = "true"; - for (int i = 0; i < 256; i++) { + for (int i = 0; i < MAX_DEPTH + 1; i++) { json = String.format(array, json); } JsonReader reader = newReader(json); - for (int i = 0; i < 255; i++) { + for (int i = 0; i < MAX_DEPTH; i++) { reader.beginObject(); assertThat(reader.nextName()).isEqualTo("a"); } @@ -1052,7 +1053,7 @@ public final class JsonUtf8ReaderTest { reader.beginObject(); fail(); } catch (JsonDataException expected) { - assertThat(expected).hasMessage("Nesting too deep at $" + repeat(".a", 255)); + assertThat(expected).hasMessage("Nesting too deep at $" + repeat(".a", MAX_DEPTH)); } } diff --git a/moshi/src/test/java/com/squareup/moshi/JsonValueReaderTest.java b/moshi/src/test/java/com/squareup/moshi/JsonValueReaderTest.java index 5efd8e9..3be3edc 100644 --- a/moshi/src/test/java/com/squareup/moshi/JsonValueReaderTest.java +++ b/moshi/src/test/java/com/squareup/moshi/JsonValueReaderTest.java @@ -24,6 +24,8 @@ import java.util.List; import java.util.Map; import org.junit.Test; +import static com.squareup.moshi.TestUtil.MAX_DEPTH; +import static com.squareup.moshi.TestUtil.repeat; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; @@ -439,29 +441,28 @@ public final class JsonValueReaderTest { @Test public void tooDeeplyNestedArrays() throws IOException { Object root = Collections.emptyList(); - for (int i = 0; i < 32; i++) { + for (int i = 0; i < MAX_DEPTH + 1; i++) { root = singletonList(root); } JsonReader reader = new JsonValueReader(root); - for (int i = 0; i < 31; i++) { + for (int i = 0; i < MAX_DEPTH; i++) { reader.beginArray(); } try { reader.beginArray(); fail(); } catch (JsonDataException expected) { - assertThat(expected).hasMessage("Nesting too deep at $[0][0][0][0][0][0][0][0][0][0][0][0][0]" - + "[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0]"); + assertThat(expected).hasMessage("Nesting too deep at $" + repeat("[0]", MAX_DEPTH + 1)); } } @Test public void tooDeeplyNestedObjects() throws IOException { Object root = Boolean.TRUE; - for (int i = 0; i < 32; i++) { + for (int i = 0; i < MAX_DEPTH + 1; i++) { root = singletonMap("a", root); } JsonReader reader = new JsonValueReader(root); - for (int i = 0; i < 31; i++) { + for (int i = 0; i < MAX_DEPTH; i++) { reader.beginObject(); assertThat(reader.nextName()).isEqualTo("a"); } @@ -469,8 +470,7 @@ public final class JsonValueReaderTest { reader.beginObject(); fail(); } catch (JsonDataException expected) { - assertThat(expected).hasMessage( - "Nesting too deep at $.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a."); + assertThat(expected).hasMessage("Nesting too deep at $" + repeat(".a", MAX_DEPTH) + "."); } } } diff --git a/moshi/src/test/java/com/squareup/moshi/JsonWriterTest.java b/moshi/src/test/java/com/squareup/moshi/JsonWriterTest.java index 5bf1b75..1a18715 100644 --- a/moshi/src/test/java/com/squareup/moshi/JsonWriterTest.java +++ b/moshi/src/test/java/com/squareup/moshi/JsonWriterTest.java @@ -25,6 +25,7 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; +import static com.squareup.moshi.TestUtil.MAX_DEPTH; import static com.squareup.moshi.TestUtil.repeat; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; @@ -423,19 +424,19 @@ public final class JsonWriterTest { @Test public void deepNestingArrays() throws IOException { JsonWriter writer = factory.newWriter(); - for (int i = 0; i < 31; i++) { + for (int i = 0; i < MAX_DEPTH; i++) { writer.beginArray(); } - for (int i = 0; i < 31; i++) { + for (int i = 0; i < MAX_DEPTH; i++) { writer.endArray(); } assertThat(factory.json()) - .isEqualTo("[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"); + .isEqualTo(repeat("[", MAX_DEPTH) + repeat("]", MAX_DEPTH)); } - @Test public void tooDeepNestingArrays() throws IOException { + @Test public void tooDeeplyNestingArrays() throws IOException { JsonWriter writer = factory.newWriter(); - for (int i = 0; i < 255; i++) { + for (int i = 0; i < MAX_DEPTH; i++) { writer.beginArray(); } try { @@ -443,29 +444,27 @@ public final class JsonWriterTest { fail(); } catch (JsonDataException expected) { assertThat(expected).hasMessage("Nesting too deep at $" - + repeat("[0]", 255) + ": circular reference?"); + + repeat("[0]", MAX_DEPTH) + ": circular reference?"); } } @Test public void deepNestingObjects() throws IOException { JsonWriter writer = factory.newWriter(); - for (int i = 0; i < 31; i++) { + for (int i = 0; i < MAX_DEPTH; i++) { writer.beginObject(); writer.name("a"); } writer.value(true); - for (int i = 0; i < 31; i++) { + for (int i = 0; i < MAX_DEPTH; i++) { writer.endObject(); } - assertThat(factory.json()).isEqualTo("" - + "{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":" - + "{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":" - + "{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":true}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"); + assertThat(factory.json()).isEqualTo( + repeat("{\"a\":", MAX_DEPTH) + "true" + repeat("}", MAX_DEPTH)); } - @Test public void tooDeepNestingObjects() throws IOException { + @Test public void tooDeeplyNestingObjects() throws IOException { JsonWriter writer = factory.newWriter(); - for (int i = 0; i < 255; i++) { + for (int i = 0; i < MAX_DEPTH; i++) { writer.beginObject(); writer.name("a"); } @@ -474,7 +473,7 @@ public final class JsonWriterTest { fail(); } catch (JsonDataException expected) { assertThat(expected).hasMessage("Nesting too deep at $" - + repeat(".a", 255) + ": circular reference?"); + + repeat(".a", MAX_DEPTH) + ": circular reference?"); } } diff --git a/moshi/src/test/java/com/squareup/moshi/TestUtil.java b/moshi/src/test/java/com/squareup/moshi/TestUtil.java index f395712..dda5933 100644 --- a/moshi/src/test/java/com/squareup/moshi/TestUtil.java +++ b/moshi/src/test/java/com/squareup/moshi/TestUtil.java @@ -19,6 +19,8 @@ import java.util.Arrays; import okio.Buffer; final class TestUtil { + static final int MAX_DEPTH = 255; + static JsonReader newReader(String json) { Buffer buffer = new Buffer().writeUtf8(json); return JsonReader.of(buffer);