diff --git a/moshi/src/main/java/com/squareup/moshi/BufferedSinkJsonWriter.java b/moshi/src/main/java/com/squareup/moshi/BufferedSinkJsonWriter.java
index e0603e4..e2ac915 100644
--- a/moshi/src/main/java/com/squareup/moshi/BufferedSinkJsonWriter.java
+++ b/moshi/src/main/java/com/squareup/moshi/BufferedSinkJsonWriter.java
@@ -57,14 +57,17 @@ final class BufferedSinkJsonWriter extends JsonWriter {
/** The output data, containing at most one top-level array or object. */
private final BufferedSink sink;
- private int[] stack = new int[32];
+ // The nesting stack. Using a manual array rather than an ArrayList saves 20%. This stack permits
+ // up to 32 levels of nesting including the top-level document. Deeper nesting is prone to trigger
+ // StackOverflowErrors.
+ private final int[] stack = new int[32];
private int stackSize = 0;
{
push(EMPTY_DOCUMENT);
}
- private String[] pathNames = new String[32];
- private int[] pathIndices = new int[32];
+ private final String[] pathNames = new String[32];
+ private final int[] pathIndices = new int[32];
/**
* A string containing a full set of spaces for a single level of
@@ -143,8 +146,8 @@ final class BufferedSinkJsonWriter extends JsonWriter {
*/
private JsonWriter open(int empty, String openBracket) throws IOException {
beforeValue();
- pathIndices[stackSize] = 0;
push(empty);
+ pathIndices[stackSize - 1] = 0;
sink.writeUtf8(openBracket);
return this;
}
@@ -175,9 +178,7 @@ final class BufferedSinkJsonWriter extends JsonWriter {
private void push(int newTop) {
if (stackSize == stack.length) {
- int[] newStack = new int[stackSize * 2];
- System.arraycopy(stack, 0, newStack, 0, stackSize);
- stack = newStack;
+ throw new JsonDataException("Nesting too deep at " + getPath() + ": circular reference?");
}
stack[stackSize++] = newTop;
}
diff --git a/moshi/src/main/java/com/squareup/moshi/BufferedSourceJsonReader.java b/moshi/src/main/java/com/squareup/moshi/BufferedSourceJsonReader.java
index 087fd91..1e19383 100644
--- a/moshi/src/main/java/com/squareup/moshi/BufferedSourceJsonReader.java
+++ b/moshi/src/main/java/com/squareup/moshi/BufferedSourceJsonReader.java
@@ -92,17 +92,17 @@ final class BufferedSourceJsonReader extends JsonReader {
*/
private String peekedString;
- /*
- * The nesting stack. Using a manual array rather than an ArrayList saves 20%.
- */
- private int[] stack = new int[32];
+ // The nesting stack. Using a manual array rather than an ArrayList saves 20%. This stack permits
+ // up to 32 levels of nesting including the top-level document. Deeper nesting is prone to trigger
+ // StackOverflowErrors.
+ private final int[] stack = new int[32];
private int stackSize = 0;
{
stack[stackSize++] = JsonScope.EMPTY_DOCUMENT;
}
- private String[] pathNames = new String[32];
- private int[] pathIndices = new int[32];
+ private final String[] pathNames = new String[32];
+ private final int[] pathIndices = new int[32];
BufferedSourceJsonReader(BufferedSource source) {
if (source == null) {
@@ -901,15 +901,7 @@ final class BufferedSourceJsonReader extends JsonReader {
private void push(int newTop) {
if (stackSize == stack.length) {
- int[] newStack = new int[stackSize * 2];
- int[] newPathIndices = new int[stackSize * 2];
- String[] newPathNames = new String[stackSize * 2];
- System.arraycopy(stack, 0, newStack, 0, stackSize);
- System.arraycopy(pathIndices, 0, newPathIndices, 0, stackSize);
- System.arraycopy(pathNames, 0, newPathNames, 0, stackSize);
- stack = newStack;
- pathIndices = newPathIndices;
- pathNames = newPathNames;
+ throw new JsonDataException("Nesting too deep at " + getPath());
}
stack[stackSize++] = newTop;
}
diff --git a/moshi/src/main/java/com/squareup/moshi/JsonDataException.java b/moshi/src/main/java/com/squareup/moshi/JsonDataException.java
index a846305..0626263 100644
--- a/moshi/src/main/java/com/squareup/moshi/JsonDataException.java
+++ b/moshi/src/main/java/com/squareup/moshi/JsonDataException.java
@@ -22,6 +22,10 @@ package com.squareup.moshi;
*
*
Exceptions of this type should be fixed by either changing the application code to accept
* the unexpected JSON, or by changing the JSON to conform to the application's expectations.
+ *
+ *
This exception may also be triggered if a document's nesting exceeds 31 levels. This depth is
+ * sufficient for all practical applications, but shallow enough to avoid uglier failures like
+ * {@link StackOverflowError}.
*/
public final class JsonDataException extends RuntimeException {
public JsonDataException() {
diff --git a/moshi/src/main/java/com/squareup/moshi/MapJsonAdapter.java b/moshi/src/main/java/com/squareup/moshi/MapJsonAdapter.java
index b0bb34d..889d1f6 100644
--- a/moshi/src/main/java/com/squareup/moshi/MapJsonAdapter.java
+++ b/moshi/src/main/java/com/squareup/moshi/MapJsonAdapter.java
@@ -50,7 +50,7 @@ final class MapJsonAdapter extends JsonAdapter