diff --git a/src/org/mozilla/javascript/json/JsonParser.java b/src/org/mozilla/javascript/json/JsonParser.java
index 5079502..0c06d26 100644
--- a/src/org/mozilla/javascript/json/JsonParser.java
+++ b/src/org/mozilla/javascript/json/JsonParser.java
@@ -86,23 +86,15 @@ public class JsonParser {
}
private Object readObject() throws ParseException {
- consumeWhitespace();
Scriptable object = cx.newObject(scope);
- // handle empty object literal case early
- if (pos < length && src.charAt(pos) == '}') {
- pos += 1;
- return object;
- }
String id;
Object value;
boolean needsComma = false;
+ consumeWhitespace();
while (pos < length) {
char c = src.charAt(pos++);
switch(c) {
case '}':
- if (!needsComma) {
- throw new ParseException("Unexpected comma in object literal");
- }
return object;
case ',':
if (!needsComma) {
@@ -136,21 +128,13 @@ public class JsonParser {
}
private Object readArray() throws ParseException {
- consumeWhitespace();
- // handle empty array literal case early
- if (pos < length && src.charAt(pos) == ']') {
- pos += 1;
- return cx.newArray(scope, 0);
- }
List<Object> list = new ArrayList<Object>();
boolean needsComma = false;
+ consumeWhitespace();
while (pos < length) {
char c = src.charAt(pos);
switch(c) {
case ']':
- if (!needsComma) {
- throw new ParseException("Unexpected comma in array literal");
- }
pos += 1;
return cx.newArray(scope, list.toArray());
case ',':
@@ -173,166 +157,108 @@ public class JsonParser {
}
private String readString() throws ParseException {
- /*
- * Optimization: if the source contains no escaped characters, create the
- * string directly from the source text.
- */
- int stringStart = pos;
+ StringBuilder b = new StringBuilder();
while (pos < length) {
char c = src.charAt(pos++);
if (c <= '\u001F') {
throw new ParseException("String contains control character");
- } else if (c == '\\') {
- break;
- } else if (c == '"') {
- return src.substring(stringStart, pos - 1);
}
- }
-
- /*
- * Slow case: string contains escaped characters. Copy a maximal sequence
- * of unescaped characters into a temporary buffer, then an escaped
- * character, and repeat until the entire string is consumed.
- */
- StringBuilder b = new StringBuilder();
- while (pos < length) {
- assert src.charAt(pos - 1) == '\\';
- b.append(src, stringStart, pos - 1);
- if (pos >= length) {
- throw new ParseException("Unterminated string");
- }
- char c = src.charAt(pos++);
- switch (c) {
- case '"':
- b.append('"');
- break;
+ switch(c) {
case '\\':
- b.append('\\');
- break;
- case '/':
- b.append('/');
- break;
- case 'b':
- b.append('\b');
- break;
- case 'f':
- b.append('\f');
- break;
- case 'n':
- b.append('\n');
- break;
- case 'r':
- b.append('\r');
- break;
- case 't':
- b.append('\t');
- break;
- case 'u':
- if (length - pos < 5) {
- throw new ParseException("Invalid character code: \\u" + src.substring(pos));
+ if (pos >= length) {
+ throw new ParseException("Unterminated string");
}
- int code = fromHex(src.charAt(pos + 0)) << 12
- | fromHex(src.charAt(pos + 1)) << 8
- | fromHex(src.charAt(pos + 2)) << 4
- | fromHex(src.charAt(pos + 3));
- if (code < 0) {
- throw new ParseException("Invalid character code: " + src.substring(pos, pos + 4));
+ c = src.charAt(pos++);
+ switch (c) {
+ case '"':
+ b.append('"');
+ break;
+ case '\\':
+ b.append('\\');
+ break;
+ case '/':
+ b.append('/');
+ break;
+ case 'b':
+ b.append('\b');
+ break;
+ case 'f':
+ b.append('\f');
+ break;
+ case 'n':
+ b.append('\n');
+ break;
+ case 'r':
+ b.append('\r');
+ break;
+ case 't':
+ b.append('\t');
+ break;
+ case 'u':
+ if (length - pos < 5) {
+ throw new ParseException("Invalid character code: \\u" + src.substring(pos));
+ }
+ try {
+ b.append((char) Integer.parseInt(src.substring(pos, pos + 4), 16));
+ pos += 4;
+ } catch (NumberFormatException nfx) {
+ throw new ParseException("Invalid character code: " + src.substring(pos, pos + 4));
+ }
+ break;
+ default:
+ throw new ParseException("Unexcpected character in string: '\\" + c + "'");
}
- pos += 4;
- b.append((char) code);
break;
+ case '"':
+ return b.toString();
default:
- throw new ParseException("Unexpected character in string: '\\" + c + "'");
- }
- stringStart = pos;
- while (pos < length) {
- c = src.charAt(pos++);
- if (c <= '\u001F') {
- throw new ParseException("String contains control character");
- } else if (c == '\\') {
+ b.append(c);
break;
- } else if (c == '"') {
- b.append(src, stringStart, pos - 1);
- return b.toString();
- }
}
}
throw new ParseException("Unterminated string literal");
}
- private int fromHex(char c) {
- return c >= '0' && c <= '9' ? c - '0'
- : c >= 'A' && c <= 'F' ? c - 'A' + 10
- : c >= 'a' && c <= 'f' ? c - 'a' + 10
- : -1;
- }
-
- private Number readNumber(char c) throws ParseException {
- assert c == '-' || (c >= '0' && c <= '9');
- final int numberStart = pos - 1;
- if (c == '-') {
- c = nextOrNumberError(numberStart);
- if (!(c >= '0' && c <= '9')) {
- throw numberError(numberStart, pos);
- }
- }
- if (c != '0') {
- readDigits();
- }
- // read optional fraction part
- if (pos < length) {
- c = src.charAt(pos);
- if (c == '.') {
- pos += 1;
- c = nextOrNumberError(numberStart);
- if (!(c >= '0' && c <= '9')) {
- throw numberError(numberStart, pos);
- }
- readDigits();
+ private Number readNumber(char first) throws ParseException {
+ StringBuilder b = new StringBuilder();
+ b.append(first);
+ while (pos < length) {
+ char c = src.charAt(pos);
+ if (!Character.isDigit(c)
+ && c != '-'
+ && c != '+'
+ && c != '.'
+ && c != 'e'
+ && c != 'E') {
+ break;
}
+ pos += 1;
+ b.append(c);
}
- // read optional exponent part
- if (pos < length) {
- c = src.charAt(pos);
- if (c == 'e' || c == 'E') {
- pos += 1;
- c = nextOrNumberError(numberStart);
- if (c == '-' || c == '+') {
- c = nextOrNumberError(numberStart);
- }
- if (!(c >= '0' && c <= '9')) {
- throw numberError(numberStart, pos);
+ String num = b.toString();
+ int numLength = num.length();
+ try {
+ // check for leading zeroes
+ for (int i = 0; i < numLength; i++) {
+ char c = num.charAt(i);
+ if (Character.isDigit(c)) {
+ if (c == '0'
+ && numLength > i + 1
+ && Character.isDigit(num.charAt(i + 1))) {
+ throw new ParseException("Unsupported number format: " + num);
+ }
+ break;
}
- readDigits();
}
- }
- String num = src.substring(numberStart, pos);
- final double dval = Double.parseDouble(num);
- final int ival = (int)dval;
- if (ival == dval) {
- return Integer.valueOf(ival);
- } else {
- return Double.valueOf(dval);
- }
- }
-
- private ParseException numberError(int start, int end) {
- return new ParseException("Unsupported number format: " + src.substring(start, end));
- }
-
- private char nextOrNumberError(int numberStart) throws ParseException {
- if (pos >= length) {
- throw numberError(numberStart, length);
- }
- return src.charAt(pos++);
- }
-
- private void readDigits() {
- for (; pos < length; ++pos) {
- char c = src.charAt(pos);
- if (!(c >= '0' && c <= '9')) {
- break;
+ final double dval = Double.parseDouble(num);
+ final int ival = (int)dval;
+ if (ival == dval) {
+ return Integer.valueOf(ival);
+ } else {
+ return Double.valueOf(dval);
}
+ } catch (NumberFormatException nfe) {
+ throw new ParseException("Unsupported number format: " + num);
}
}
diff --git a/testsrc/org/mozilla/javascript/tests/json/JsonParserTest.java b/testsrc/org/mozilla/javascript/tests/json/JsonParserTest.java
index ee885ae..2783b50 100644
--- a/testsrc/org/mozilla/javascript/tests/json/JsonParserTest.java
+++ b/testsrc/org/mozilla/javascript/tests/json/JsonParserTest.java
@@ -196,66 +196,6 @@ public class JsonParserTest {
parser.parseValue("[1 ");
}
- @Test(expected = ParseException.class)
- public void shouldFailToParseIllegalUnicodeEscapeSeq() throws Exception {
- parser.parseValue("\"\\u-123\"");
- }
-
- @Test(expected = ParseException.class)
- public void shouldFailToParseIllegalUnicodeEscapeSeq2() throws Exception {
- parser.parseValue("\"\\u006\u0661\"");
- }
-
- @Test(expected = ParseException.class)
- public void shouldFailToParseIllegalUnicodeEscapeSeq3() throws Exception {
- parser.parseValue("\"\\u006ูก\"");
- }
-
- @Test(expected = ParseException.class)
- public void shouldFailToParseTrailingCommaInObject1() throws Exception {
- parser.parseValue("{\"a\": 1,}");
- }
-
- @Test(expected = ParseException.class)
- public void shouldFailToParseTrailingCommaInObject2() throws Exception {
- parser.parseValue("{,\"a\": 1}");
- }
-
- @Test(expected = ParseException.class)
- public void shouldFailToParseTrailingCommaInObject3() throws Exception {
- parser.parseValue("{,}");
- }
-
- @Test
- public void shouldParseEmptyObject() throws Exception {
- parser.parseValue("{}");
- }
-
- @Test(expected = ParseException.class)
- public void shouldFailToParseTrailingCommaInArray1() throws Exception {
- parser.parseValue("[1,]");
- }
-
- @Test(expected = ParseException.class)
- public void shouldFailToParseTrailingCommaInArray2() throws Exception {
- parser.parseValue("[,1]");
- }
-
- @Test(expected = ParseException.class)
- public void shouldFailToParseTrailingCommaInArray3() throws Exception {
- parser.parseValue("[,]");
- }
-
- @Test
- public void shouldParseEmptyArray() throws Exception {
- parser.parseValue("[]");
- }
-
- @Test(expected = ParseException.class)
- public void shouldFailToParseIllegalNumber() throws Exception {
- parser.parseValue("1.");
- }
-
private String str(char... chars) {
return new String(chars);
}