Support non-execute prefixes.
This commit is contained in:
parent
97cb326ad2
commit
b96d2d9837
@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package com.google.gson.stream;
|
package com.google.gson.stream;
|
||||||
|
|
||||||
|
import com.google.gson.internal.JsonReaderInternalAccess;
|
||||||
|
import com.google.gson.internal.bind.JsonTreeReader;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -186,6 +188,9 @@ import java.io.Reader;
|
|||||||
* @since 1.6
|
* @since 1.6
|
||||||
*/
|
*/
|
||||||
public class JsonReader2 implements Closeable {
|
public class JsonReader2 implements Closeable {
|
||||||
|
/** The only non-execute prefix this parser permits */
|
||||||
|
private static final char[] NON_EXECUTE_PREFIX = ")]}'\n".toCharArray();
|
||||||
|
|
||||||
private static final int PEEKED_NONE = 0;
|
private static final int PEEKED_NONE = 0;
|
||||||
private static final int PEEKED_BEGIN_OBJECT = 1;
|
private static final int PEEKED_BEGIN_OBJECT = 1;
|
||||||
private static final int PEEKED_END_OBJECT = 2;
|
private static final int PEEKED_END_OBJECT = 2;
|
||||||
@ -495,6 +500,9 @@ public class JsonReader2 implements Closeable {
|
|||||||
throw syntaxError("Expected ':'");
|
throw syntaxError("Expected ':'");
|
||||||
}
|
}
|
||||||
} else if (peekStack == JsonScope.EMPTY_DOCUMENT) {
|
} else if (peekStack == JsonScope.EMPTY_DOCUMENT) {
|
||||||
|
if (lenient) {
|
||||||
|
consumeNonExecutePrefix();
|
||||||
|
}
|
||||||
stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;
|
stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;
|
||||||
} else if (peekStack == JsonScope.NONEMPTY_DOCUMENT) {
|
} else if (peekStack == JsonScope.NONEMPTY_DOCUMENT) {
|
||||||
int c = nextNonWhitespace(false);
|
int c = nextNonWhitespace(false);
|
||||||
@ -1412,6 +1420,28 @@ public class JsonReader2 implements Closeable {
|
|||||||
+ " at line " + getLineNumber() + " column " + getColumnNumber());
|
+ " at line " + getLineNumber() + " column " + getColumnNumber());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consumes the non-execute prefix if it exists.
|
||||||
|
*/
|
||||||
|
private void consumeNonExecutePrefix() throws IOException {
|
||||||
|
// fast forward through the leading whitespace
|
||||||
|
nextNonWhitespace(true);
|
||||||
|
pos--;
|
||||||
|
|
||||||
|
if (pos + NON_EXECUTE_PREFIX.length > limit && !fillBuffer(NON_EXECUTE_PREFIX.length)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < NON_EXECUTE_PREFIX.length; i++) {
|
||||||
|
if (buffer[pos + i] != NON_EXECUTE_PREFIX[i]) {
|
||||||
|
return; // not a security token!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we consumed a security token!
|
||||||
|
pos += NON_EXECUTE_PREFIX.length;
|
||||||
|
}
|
||||||
|
|
||||||
/*static {
|
/*static {
|
||||||
JsonReaderInternalAccess.INSTANCE = new JsonReaderInternalAccess() {
|
JsonReaderInternalAccess.INSTANCE = new JsonReaderInternalAccess() {
|
||||||
@Override public void promoteNameToValue(JsonReader reader) throws IOException {
|
@Override public void promoteNameToValue(JsonReader reader) throws IOException {
|
||||||
|
@ -16,18 +16,23 @@
|
|||||||
|
|
||||||
package com.google.gson.stream;
|
package com.google.gson.stream;
|
||||||
|
|
||||||
import com.google.gson.JsonSyntaxException;
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
public final class JsonReaderTest extends TestCase {
|
import static com.google.gson.stream.JsonToken.BEGIN_ARRAY;
|
||||||
|
import static com.google.gson.stream.JsonToken.BEGIN_OBJECT;
|
||||||
|
import static com.google.gson.stream.JsonToken.BOOLEAN;
|
||||||
|
import static com.google.gson.stream.JsonToken.END_ARRAY;
|
||||||
|
import static com.google.gson.stream.JsonToken.END_OBJECT;
|
||||||
|
import static com.google.gson.stream.JsonToken.NAME;
|
||||||
|
import static com.google.gson.stream.JsonToken.NULL;
|
||||||
|
import static com.google.gson.stream.JsonToken.NUMBER;
|
||||||
|
import static com.google.gson.stream.JsonToken.STRING;
|
||||||
|
|
||||||
|
public final class JsonReaderTest extends TestCase {
|
||||||
public void testReadArray() throws IOException {
|
public void testReadArray() throws IOException {
|
||||||
JsonReader reader = new JsonReader(new StringReader("[true, true]"));
|
JsonReader reader = new JsonReader(new StringReader("[true, true]"));
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
@ -398,7 +403,7 @@ public final class JsonReaderTest extends TestCase {
|
|||||||
JsonReader reader = new JsonReader(new StringReader("[truey]"));
|
JsonReader reader = new JsonReader(new StringReader("[truey]"));
|
||||||
reader.setLenient(true);
|
reader.setLenient(true);
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
assertEquals(JsonToken.STRING, reader.peek());
|
assertEquals(STRING, reader.peek());
|
||||||
try {
|
try {
|
||||||
reader.nextBoolean();
|
reader.nextBoolean();
|
||||||
fail();
|
fail();
|
||||||
@ -412,7 +417,7 @@ public final class JsonReaderTest extends TestCase {
|
|||||||
JsonReader reader = new JsonReader(new StringReader("[12.34e5x]"));
|
JsonReader reader = new JsonReader(new StringReader("[12.34e5x]"));
|
||||||
reader.setLenient(true);
|
reader.setLenient(true);
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
assertEquals(JsonToken.STRING, reader.peek());
|
assertEquals(STRING, reader.peek());
|
||||||
try {
|
try {
|
||||||
reader.nextInt();
|
reader.nextInt();
|
||||||
fail();
|
fail();
|
||||||
@ -425,7 +430,7 @@ public final class JsonReaderTest extends TestCase {
|
|||||||
JsonReader reader = new JsonReader(new StringReader("[-9223372036854775808]"));
|
JsonReader reader = new JsonReader(new StringReader("[-9223372036854775808]"));
|
||||||
reader.setLenient(true);
|
reader.setLenient(true);
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
assertEquals(JsonToken.NUMBER, reader.peek());
|
assertEquals(NUMBER, reader.peek());
|
||||||
assertEquals(-9223372036854775808L, reader.nextLong());
|
assertEquals(-9223372036854775808L, reader.nextLong());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -437,7 +442,7 @@ public final class JsonReaderTest extends TestCase {
|
|||||||
JsonReader reader = new JsonReader(new StringReader("[-9223372036854775809]"));
|
JsonReader reader = new JsonReader(new StringReader("[-9223372036854775809]"));
|
||||||
reader.setLenient(true);
|
reader.setLenient(true);
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
assertEquals(JsonToken.NUMBER, reader.peek());
|
assertEquals(NUMBER, reader.peek());
|
||||||
try {
|
try {
|
||||||
reader.nextLong();
|
reader.nextLong();
|
||||||
fail();
|
fail();
|
||||||
@ -450,7 +455,7 @@ public final class JsonReaderTest extends TestCase {
|
|||||||
JsonReader reader = new JsonReader(new StringReader("[-92233720368547758080]"));
|
JsonReader reader = new JsonReader(new StringReader("[-92233720368547758080]"));
|
||||||
reader.setLenient(true);
|
reader.setLenient(true);
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
assertEquals(JsonToken.NUMBER, reader.peek());
|
assertEquals(NUMBER, reader.peek());
|
||||||
try {
|
try {
|
||||||
reader.nextLong();
|
reader.nextLong();
|
||||||
fail();
|
fail();
|
||||||
@ -463,7 +468,7 @@ public final class JsonReaderTest extends TestCase {
|
|||||||
JsonReader reader = new JsonReader(new StringReader("[\"12\u00334\"]"));
|
JsonReader reader = new JsonReader(new StringReader("[\"12\u00334\"]"));
|
||||||
reader.setLenient(true);
|
reader.setLenient(true);
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
assertEquals(JsonToken.STRING, reader.peek());
|
assertEquals(STRING, reader.peek());
|
||||||
assertEquals(1234, reader.nextInt());
|
assertEquals(1234, reader.nextInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1316,29 +1321,29 @@ public final class JsonReaderTest extends TestCase {
|
|||||||
JsonReader reader = new JsonReader(new StringReader("[123e]"));
|
JsonReader reader = new JsonReader(new StringReader("[123e]"));
|
||||||
reader.setLenient(true);
|
reader.setLenient(true);
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
assertEquals(JsonToken.STRING, reader.peek());
|
assertEquals(STRING, reader.peek());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testStringAsNumberWithDigitAndNonDigitExponent() throws IOException {
|
public void testStringAsNumberWithDigitAndNonDigitExponent() throws IOException {
|
||||||
JsonReader reader = new JsonReader(new StringReader("[123e4b]"));
|
JsonReader reader = new JsonReader(new StringReader("[123e4b]"));
|
||||||
reader.setLenient(true);
|
reader.setLenient(true);
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
assertEquals(JsonToken.STRING, reader.peek());
|
assertEquals(STRING, reader.peek());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testStringAsNumberWithNonDigitExponent() throws IOException {
|
public void testStringAsNumberWithNonDigitExponent() throws IOException {
|
||||||
JsonReader reader = new JsonReader(new StringReader("[123eb]"));
|
JsonReader reader = new JsonReader(new StringReader("[123eb]"));
|
||||||
reader.setLenient(true);
|
reader.setLenient(true);
|
||||||
reader.beginArray();
|
reader.beginArray();
|
||||||
assertEquals(JsonToken.STRING, reader.peek());
|
assertEquals(STRING, reader.peek());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEmptyStringName() throws IOException {
|
public void testEmptyStringName() throws IOException {
|
||||||
JsonReader reader = new JsonReader(new StringReader("{\"\":true}"));
|
JsonReader reader = new JsonReader(new StringReader("{\"\":true}"));
|
||||||
reader.setLenient(true);
|
reader.setLenient(true);
|
||||||
assertEquals(JsonToken.BEGIN_OBJECT, reader.peek());
|
assertEquals(BEGIN_OBJECT, reader.peek());
|
||||||
reader.beginObject();
|
reader.beginObject();
|
||||||
assertEquals(JsonToken.NAME, reader.peek());
|
assertEquals(NAME, reader.peek());
|
||||||
assertEquals("", reader.nextName());
|
assertEquals("", reader.nextName());
|
||||||
assertEquals(JsonToken.BOOLEAN, reader.peek());
|
assertEquals(JsonToken.BOOLEAN, reader.peek());
|
||||||
assertEquals(true, reader.nextBoolean());
|
assertEquals(true, reader.nextBoolean());
|
||||||
@ -1377,4 +1382,98 @@ public final class JsonReaderTest extends TestCase {
|
|||||||
Arrays.fill(array, c);
|
Arrays.fill(array, c);
|
||||||
return new String(array);
|
return new String(array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testMalformedDocuments() throws IOException {
|
||||||
|
assertDocument("{]", BEGIN_OBJECT, IOException.class);
|
||||||
|
assertDocument("{,", BEGIN_OBJECT, IOException.class);
|
||||||
|
assertDocument("{{", BEGIN_OBJECT, IOException.class);
|
||||||
|
assertDocument("{[", BEGIN_OBJECT, IOException.class);
|
||||||
|
assertDocument("{:", BEGIN_OBJECT, IOException.class);
|
||||||
|
assertDocument("{\"name\",", BEGIN_OBJECT, NAME, IOException.class);
|
||||||
|
assertDocument("{\"name\",", BEGIN_OBJECT, NAME, IOException.class);
|
||||||
|
assertDocument("{\"name\":}", BEGIN_OBJECT, NAME, IOException.class);
|
||||||
|
assertDocument("{\"name\"::", BEGIN_OBJECT, NAME, IOException.class);
|
||||||
|
assertDocument("{\"name\":,", BEGIN_OBJECT, NAME, IOException.class);
|
||||||
|
assertDocument("{\"name\"=}", BEGIN_OBJECT, NAME, IOException.class);
|
||||||
|
assertDocument("{\"name\"=>}", BEGIN_OBJECT, NAME, IOException.class);
|
||||||
|
assertDocument("{\"name\"=>\"string\":", BEGIN_OBJECT, NAME, STRING, IOException.class);
|
||||||
|
assertDocument("{\"name\"=>\"string\"=", BEGIN_OBJECT, NAME, STRING, IOException.class);
|
||||||
|
assertDocument("{\"name\"=>\"string\"=>", BEGIN_OBJECT, NAME, STRING, IOException.class);
|
||||||
|
assertDocument("{\"name\"=>\"string\",", BEGIN_OBJECT, NAME, STRING, IOException.class);
|
||||||
|
assertDocument("{\"name\"=>\"string\",\"name\"", BEGIN_OBJECT, NAME, STRING, NAME);
|
||||||
|
assertDocument("[}", BEGIN_ARRAY, IOException.class);
|
||||||
|
assertDocument("[,]", BEGIN_ARRAY, NULL, NULL, END_ARRAY);
|
||||||
|
assertDocument("{", BEGIN_OBJECT, IOException.class);
|
||||||
|
assertDocument("{\"name\"", BEGIN_OBJECT, NAME, IOException.class);
|
||||||
|
assertDocument("{\"name\",", BEGIN_OBJECT, NAME, IOException.class);
|
||||||
|
assertDocument("{'name'", BEGIN_OBJECT, NAME, IOException.class);
|
||||||
|
assertDocument("{'name',", BEGIN_OBJECT, NAME, IOException.class);
|
||||||
|
assertDocument("{name", BEGIN_OBJECT, NAME, IOException.class);
|
||||||
|
assertDocument("[", BEGIN_ARRAY, IOException.class);
|
||||||
|
assertDocument("[string", BEGIN_ARRAY, STRING, IOException.class);
|
||||||
|
assertDocument("[\"string\"", BEGIN_ARRAY, STRING, IOException.class);
|
||||||
|
assertDocument("['string'", BEGIN_ARRAY, STRING, IOException.class);
|
||||||
|
assertDocument("[123", BEGIN_ARRAY, NUMBER, IOException.class);
|
||||||
|
assertDocument("[123,", BEGIN_ARRAY, NUMBER, IOException.class);
|
||||||
|
assertDocument("{\"name\":123", BEGIN_OBJECT, NAME, NUMBER, IOException.class);
|
||||||
|
assertDocument("{\"name\":123,", BEGIN_OBJECT, NAME, NUMBER, IOException.class);
|
||||||
|
assertDocument("{\"name\":\"string\"", BEGIN_OBJECT, NAME, STRING, IOException.class);
|
||||||
|
assertDocument("{\"name\":\"string\",", BEGIN_OBJECT, NAME, STRING, IOException.class);
|
||||||
|
assertDocument("{\"name\":'string'", BEGIN_OBJECT, NAME, STRING, IOException.class);
|
||||||
|
assertDocument("{\"name\":'string',", BEGIN_OBJECT, NAME, STRING, IOException.class);
|
||||||
|
assertDocument("{\"name\":false", BEGIN_OBJECT, NAME, BOOLEAN, IOException.class);
|
||||||
|
assertDocument("{\"name\":false,,", BEGIN_OBJECT, NAME, BOOLEAN, IOException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This test behave slightly differently in Gson 2.2 and earlier. It fails
|
||||||
|
* during peek rather than during nextString().
|
||||||
|
*/
|
||||||
|
public void testUnterminatedStringFailure() throws IOException {
|
||||||
|
JsonReader reader = new JsonReader(new StringReader("[\"string"));
|
||||||
|
reader.setLenient(true);
|
||||||
|
reader.beginArray();
|
||||||
|
assertEquals(JsonToken.STRING, reader.peek());
|
||||||
|
try {
|
||||||
|
reader.nextString();
|
||||||
|
fail();
|
||||||
|
} catch (MalformedJsonException expected) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertDocument(String document, Object... expectations) throws IOException {
|
||||||
|
JsonReader reader = new JsonReader(new StringReader(document));
|
||||||
|
reader.setLenient(true);
|
||||||
|
for (Object expectation : expectations) {
|
||||||
|
if (expectation == BEGIN_OBJECT) {
|
||||||
|
reader.beginObject();
|
||||||
|
} else if (expectation == BEGIN_ARRAY) {
|
||||||
|
reader.beginArray();
|
||||||
|
} else if (expectation == END_OBJECT) {
|
||||||
|
reader.endObject();
|
||||||
|
} else if (expectation == END_ARRAY) {
|
||||||
|
reader.endArray();
|
||||||
|
} else if (expectation == NAME) {
|
||||||
|
assertEquals("name", reader.nextName());
|
||||||
|
} else if (expectation == BOOLEAN) {
|
||||||
|
assertEquals(false, reader.nextBoolean());
|
||||||
|
} else if (expectation == STRING) {
|
||||||
|
assertEquals("string", reader.nextString());
|
||||||
|
} else if (expectation == NUMBER) {
|
||||||
|
assertEquals(123, reader.nextInt());
|
||||||
|
} else if (expectation == NULL) {
|
||||||
|
reader.nextNull();
|
||||||
|
} else if (expectation == IOException.class) {
|
||||||
|
try {
|
||||||
|
reader.peek();
|
||||||
|
fail();
|
||||||
|
} catch (IOException expected) {
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test character-by-character reads
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user