Fix JsonValueReader to support up to 255 levels of nesting. (#417)
Follow up to https://github.com/square/moshi/pull/349
This commit is contained in:
parent
d2ef4b5a61
commit
359244e996
5 changed files with 40 additions and 32 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) + ".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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?");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue