mirror of
https://github.com/fankes/moshi.git
synced 2025-10-19 07:59:21 +08:00
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:
@@ -49,7 +49,7 @@ final class JsonValueReader extends JsonReader {
|
|||||||
/** Sentinel object pushed on {@link #stack} when the reader is closed. */
|
/** Sentinel object pushed on {@link #stack} when the reader is closed. */
|
||||||
private static final Object JSON_READER_CLOSED = new Object();
|
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) {
|
JsonValueReader(Object root) {
|
||||||
scopes[stackSize] = JsonScope.NONEMPTY_DOCUMENT;
|
scopes[stackSize] = JsonScope.NONEMPTY_DOCUMENT;
|
||||||
@@ -308,7 +308,13 @@ final class JsonValueReader extends JsonReader {
|
|||||||
|
|
||||||
private void push(Object newTop) {
|
private void push(Object newTop) {
|
||||||
if (stackSize == stack.length) {
|
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;
|
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.NULL;
|
||||||
import static com.squareup.moshi.JsonReader.Token.NUMBER;
|
import static com.squareup.moshi.JsonReader.Token.NUMBER;
|
||||||
import static com.squareup.moshi.JsonReader.Token.STRING;
|
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.newReader;
|
||||||
import static com.squareup.moshi.TestUtil.repeat;
|
import static com.squareup.moshi.TestUtil.repeat;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
@@ -1023,28 +1024,28 @@ public final class JsonUtf8ReaderTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test public void tooDeeplyNestedArrays() throws IOException {
|
@Test public void tooDeeplyNestedArrays() throws IOException {
|
||||||
JsonReader reader = newReader(repeat("[", 256) + repeat("]", 256));
|
JsonReader reader = newReader(repeat("[", MAX_DEPTH + 1) + repeat("]", MAX_DEPTH + 1));
|
||||||
for (int i = 0; i < 255; i++) {
|
for (int i = 0; i < MAX_DEPTH; i++) {
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
fail();
|
fail();
|
||||||
} catch (JsonDataException expected) {
|
} 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 {
|
@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 array = "{\"a\":%s}";
|
||||||
String json = "true";
|
String json = "true";
|
||||||
for (int i = 0; i < 256; i++) {
|
for (int i = 0; i < MAX_DEPTH + 1; i++) {
|
||||||
json = String.format(array, json);
|
json = String.format(array, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonReader reader = newReader(json);
|
JsonReader reader = newReader(json);
|
||||||
for (int i = 0; i < 255; i++) {
|
for (int i = 0; i < MAX_DEPTH; i++) {
|
||||||
reader.beginObject();
|
reader.beginObject();
|
||||||
assertThat(reader.nextName()).isEqualTo("a");
|
assertThat(reader.nextName()).isEqualTo("a");
|
||||||
}
|
}
|
||||||
@@ -1052,7 +1053,7 @@ public final class JsonUtf8ReaderTest {
|
|||||||
reader.beginObject();
|
reader.beginObject();
|
||||||
fail();
|
fail();
|
||||||
} catch (JsonDataException expected) {
|
} 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 java.util.Map;
|
||||||
import org.junit.Test;
|
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.singletonList;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
@@ -439,29 +441,28 @@ public final class JsonValueReaderTest {
|
|||||||
|
|
||||||
@Test public void tooDeeplyNestedArrays() throws IOException {
|
@Test public void tooDeeplyNestedArrays() throws IOException {
|
||||||
Object root = Collections.emptyList();
|
Object root = Collections.emptyList();
|
||||||
for (int i = 0; i < 32; i++) {
|
for (int i = 0; i < MAX_DEPTH + 1; i++) {
|
||||||
root = singletonList(root);
|
root = singletonList(root);
|
||||||
}
|
}
|
||||||
JsonReader reader = new JsonValueReader(root);
|
JsonReader reader = new JsonValueReader(root);
|
||||||
for (int i = 0; i < 31; i++) {
|
for (int i = 0; i < MAX_DEPTH; i++) {
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
fail();
|
fail();
|
||||||
} catch (JsonDataException expected) {
|
} catch (JsonDataException expected) {
|
||||||
assertThat(expected).hasMessage("Nesting too deep at $[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));
|
||||||
+ "[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0]");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void tooDeeplyNestedObjects() throws IOException {
|
@Test public void tooDeeplyNestedObjects() throws IOException {
|
||||||
Object root = Boolean.TRUE;
|
Object root = Boolean.TRUE;
|
||||||
for (int i = 0; i < 32; i++) {
|
for (int i = 0; i < MAX_DEPTH + 1; i++) {
|
||||||
root = singletonMap("a", root);
|
root = singletonMap("a", root);
|
||||||
}
|
}
|
||||||
JsonReader reader = new JsonValueReader(root);
|
JsonReader reader = new JsonValueReader(root);
|
||||||
for (int i = 0; i < 31; i++) {
|
for (int i = 0; i < MAX_DEPTH; i++) {
|
||||||
reader.beginObject();
|
reader.beginObject();
|
||||||
assertThat(reader.nextName()).isEqualTo("a");
|
assertThat(reader.nextName()).isEqualTo("a");
|
||||||
}
|
}
|
||||||
@@ -469,8 +470,7 @@ public final class JsonValueReaderTest {
|
|||||||
reader.beginObject();
|
reader.beginObject();
|
||||||
fail();
|
fail();
|
||||||
} catch (JsonDataException expected) {
|
} catch (JsonDataException expected) {
|
||||||
assertThat(expected).hasMessage(
|
assertThat(expected).hasMessage("Nesting too deep at $" + repeat(".a", MAX_DEPTH) + ".");
|
||||||
"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.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -25,6 +25,7 @@ import org.junit.runners.Parameterized;
|
|||||||
import org.junit.runners.Parameterized.Parameter;
|
import org.junit.runners.Parameterized.Parameter;
|
||||||
import org.junit.runners.Parameterized.Parameters;
|
import org.junit.runners.Parameterized.Parameters;
|
||||||
|
|
||||||
|
import static com.squareup.moshi.TestUtil.MAX_DEPTH;
|
||||||
import static com.squareup.moshi.TestUtil.repeat;
|
import static com.squareup.moshi.TestUtil.repeat;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
@@ -423,19 +424,19 @@ public final class JsonWriterTest {
|
|||||||
|
|
||||||
@Test public void deepNestingArrays() throws IOException {
|
@Test public void deepNestingArrays() throws IOException {
|
||||||
JsonWriter writer = factory.newWriter();
|
JsonWriter writer = factory.newWriter();
|
||||||
for (int i = 0; i < 31; i++) {
|
for (int i = 0; i < MAX_DEPTH; i++) {
|
||||||
writer.beginArray();
|
writer.beginArray();
|
||||||
}
|
}
|
||||||
for (int i = 0; i < 31; i++) {
|
for (int i = 0; i < MAX_DEPTH; i++) {
|
||||||
writer.endArray();
|
writer.endArray();
|
||||||
}
|
}
|
||||||
assertThat(factory.json())
|
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();
|
JsonWriter writer = factory.newWriter();
|
||||||
for (int i = 0; i < 255; i++) {
|
for (int i = 0; i < MAX_DEPTH; i++) {
|
||||||
writer.beginArray();
|
writer.beginArray();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -443,29 +444,27 @@ public final class JsonWriterTest {
|
|||||||
fail();
|
fail();
|
||||||
} catch (JsonDataException expected) {
|
} catch (JsonDataException expected) {
|
||||||
assertThat(expected).hasMessage("Nesting too deep at $"
|
assertThat(expected).hasMessage("Nesting too deep at $"
|
||||||
+ repeat("[0]", 255) + ": circular reference?");
|
+ repeat("[0]", MAX_DEPTH) + ": circular reference?");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void deepNestingObjects() throws IOException {
|
@Test public void deepNestingObjects() throws IOException {
|
||||||
JsonWriter writer = factory.newWriter();
|
JsonWriter writer = factory.newWriter();
|
||||||
for (int i = 0; i < 31; i++) {
|
for (int i = 0; i < MAX_DEPTH; i++) {
|
||||||
writer.beginObject();
|
writer.beginObject();
|
||||||
writer.name("a");
|
writer.name("a");
|
||||||
}
|
}
|
||||||
writer.value(true);
|
writer.value(true);
|
||||||
for (int i = 0; i < 31; i++) {
|
for (int i = 0; i < MAX_DEPTH; i++) {
|
||||||
writer.endObject();
|
writer.endObject();
|
||||||
}
|
}
|
||||||
assertThat(factory.json()).isEqualTo(""
|
assertThat(factory.json()).isEqualTo(
|
||||||
+ "{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":"
|
repeat("{\"a\":", MAX_DEPTH) + "true" + repeat("}", MAX_DEPTH));
|
||||||
+ "{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":"
|
|
||||||
+ "{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":true}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void tooDeepNestingObjects() throws IOException {
|
@Test public void tooDeeplyNestingObjects() throws IOException {
|
||||||
JsonWriter writer = factory.newWriter();
|
JsonWriter writer = factory.newWriter();
|
||||||
for (int i = 0; i < 255; i++) {
|
for (int i = 0; i < MAX_DEPTH; i++) {
|
||||||
writer.beginObject();
|
writer.beginObject();
|
||||||
writer.name("a");
|
writer.name("a");
|
||||||
}
|
}
|
||||||
@@ -474,7 +473,7 @@ public final class JsonWriterTest {
|
|||||||
fail();
|
fail();
|
||||||
} catch (JsonDataException expected) {
|
} catch (JsonDataException expected) {
|
||||||
assertThat(expected).hasMessage("Nesting too deep at $"
|
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;
|
import okio.Buffer;
|
||||||
|
|
||||||
final class TestUtil {
|
final class TestUtil {
|
||||||
|
static final int MAX_DEPTH = 255;
|
||||||
|
|
||||||
static JsonReader newReader(String json) {
|
static JsonReader newReader(String json) {
|
||||||
Buffer buffer = new Buffer().writeUtf8(json);
|
Buffer buffer = new Buffer().writeUtf8(json);
|
||||||
return JsonReader.of(buffer);
|
return JsonReader.of(buffer);
|
||||||
|
Reference in New Issue
Block a user