8015346: JSON parsing issues with escaped strings, octal, decimal numbers
Reviewed-by: hannesw, jlaskey
--- a/nashorn/src/jdk/nashorn/internal/ir/BlockLexicalContext.java Wed Jun 05 12:41:09 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/ir/BlockLexicalContext.java Thu Jun 06 21:41:20 2013 +0530
@@ -63,6 +63,7 @@
return sstack.pop();
}
+ @SuppressWarnings("unchecked")
@Override
public <T extends LexicalContextNode> T pop(final T node) {
T expected = node;
--- a/nashorn/src/jdk/nashorn/internal/parser/JSONParser.java Wed Jun 05 12:41:09 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/parser/JSONParser.java Thu Jun 06 21:41:20 2013 +0530
@@ -54,10 +54,9 @@
* Constructor
* @param source the source
* @param errors the error manager
- * @param strict are we in strict mode
*/
- public JSONParser(final Source source, final ErrorManager errors, final boolean strict) {
- super(source, errors, strict);
+ public JSONParser(final Source source, final ErrorManager errors) {
+ super(source, errors, false);
}
/**
@@ -135,6 +134,7 @@
return ch == '\"';
}
+ // ECMA 15.12.1.1 The JSON Lexical Grammar - JSONWhiteSpace
@Override
protected boolean isWhitespace(final char ch) {
return Lexer.isJsonWhitespace(ch);
@@ -144,6 +144,99 @@
protected boolean isEOL(final char ch) {
return Lexer.isJsonEOL(ch);
}
+
+ // ECMA 15.12.1.1 The JSON Lexical Grammar - JSONNumber
+ @Override
+ protected void scanNumber() {
+ // Record beginning of number.
+ final int start = position;
+ // Assume value is a decimal.
+ TokenType type = TokenType.DECIMAL;
+
+ // floating point can't start with a "." with no leading digit before
+ if (ch0 == '.') {
+ error(Lexer.message("json.invalid.number"), STRING, position, limit);
+ }
+
+ // First digit of number.
+ int digit = convertDigit(ch0, 10);
+
+ // skip first digit
+ skip(1);
+
+ if (digit != 0) {
+ // Skip over remaining digits.
+ while (convertDigit(ch0, 10) != -1) {
+ skip(1);
+ }
+ }
+
+ if (ch0 == '.' || ch0 == 'E' || ch0 == 'e') {
+ // Must be a double.
+ if (ch0 == '.') {
+ // Skip period.
+ skip(1);
+
+ boolean mantissa = false;
+ // Skip mantissa.
+ while (convertDigit(ch0, 10) != -1) {
+ mantissa = true;
+ skip(1);
+ }
+
+ if (! mantissa) {
+ // no digit after "."
+ error(Lexer.message("json.invalid.number"), STRING, position, limit);
+ }
+ }
+
+ // Detect exponent.
+ if (ch0 == 'E' || ch0 == 'e') {
+ // Skip E.
+ skip(1);
+ // Detect and skip exponent sign.
+ if (ch0 == '+' || ch0 == '-') {
+ skip(1);
+ }
+ boolean exponent = false;
+ // Skip exponent.
+ while (convertDigit(ch0, 10) != -1) {
+ exponent = true;
+ skip(1);
+ }
+
+ if (! exponent) {
+ // no digit after "E"
+ error(Lexer.message("json.invalid.number"), STRING, position, limit);
+ }
+ }
+
+ type = TokenType.FLOATING;
+ }
+
+ // Add number token.
+ add(type, start);
+ }
+
+ // ECMA 15.12.1.1 The JSON Lexical Grammar - JSONEscapeCharacter
+ @Override
+ protected boolean isEscapeCharacter(final char ch) {
+ switch (ch) {
+ case '"':
+ case '/':
+ case '\\':
+ case 'b':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+ // could be unicode escape
+ case 'u':
+ return true;
+ default:
+ return false;
+ }
+ }
};
k = -1;
--- a/nashorn/src/jdk/nashorn/internal/parser/Lexer.java Wed Jun 05 12:41:09 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/parser/Lexer.java Thu Jun 06 21:41:20 2013 +0530
@@ -648,7 +648,7 @@
*
* @return The converted digit or -1 if invalid.
*/
- private static int convertDigit(final char ch, final int base) {
+ protected static int convertDigit(final char ch, final int base) {
int digit;
if ('0' <= ch && ch <= '9') {
@@ -908,7 +908,7 @@
/**
* Scan over a string literal.
*/
- private void scanString(final boolean add) {
+ protected void scanString(final boolean add) {
// Type of string.
TokenType type = STRING;
// Record starting quote.
@@ -925,6 +925,9 @@
if (ch0 == '\\') {
type = ESCSTRING;
skip(1);
+ if (! isEscapeCharacter(ch0)) {
+ error(Lexer.message("invalid.escape.char"), STRING, position, limit);
+ }
if (isEOL(ch0)) {
// Multiline string literal
skipEOL(false);
@@ -979,6 +982,16 @@
}
/**
+ * Is the given character a valid escape char after "\" ?
+ *
+ * @param ch character to be checked
+ * @return if the given character is valid after "\"
+ */
+ protected boolean isEscapeCharacter(final char ch) {
+ return true;
+ }
+
+ /**
* Convert string to number.
*
* @param valueString String to convert.
@@ -1024,7 +1037,7 @@
/**
* Scan a number.
*/
- private void scanNumber() {
+ protected void scanNumber() {
// Record beginning of number.
final int start = position;
// Assume value is a decimal.
@@ -1583,7 +1596,7 @@
return null;
}
- private static String message(final String msgId, final String... args) {
+ protected static String message(final String msgId, final String... args) {
return ECMAErrors.getMessage("lexer.error." + msgId, args);
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/JSONFunctions.java Wed Jun 05 12:41:09 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/runtime/JSONFunctions.java Thu Jun 06 21:41:20 2013 +0530
@@ -66,13 +66,9 @@
*/
public static Object parse(final Object text, final Object reviver) {
final String str = JSType.toString(text);
- final Context context = Context.getContextTrusted();
final JSONParser parser = new JSONParser(
new Source("<json>", str),
- new Context.ThrowErrorManager(),
- (context != null) ?
- context.getEnv()._strict :
- false);
+ new Context.ThrowErrorManager());
Node node;
--- a/nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties Wed Jun 05 12:41:09 2013 -0300
+++ b/nashorn/src/jdk/nashorn/internal/runtime/resources/Messages.properties Thu Jun 06 21:41:20 2013 +0530
@@ -28,6 +28,8 @@
lexer.error.invalid.hex=Invalid hex digit
lexer.error.invalid.octal=Invalid octal digit
lexer.error.strict.no.octal=cannot use octal escapes in strict mode
+lexer.error.json.invalid.number=Invalid JSON number format
+lexer.error.invalid.escape.char=Invalid escape character
lexer.error.illegal.identifier.character=Illegal character in identifier
parser.error.illegal.continue.stmt=Illegal continue statement
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8015346.js Thu Jun 06 21:41:20 2013 +0530
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8015346: JSON parsing issues with escaped strings, octal, decimal numbers *
+ * @test
+ * @run
+ */
+
+function checkJSON(str) {
+ try {
+ JSON.parse(str);
+ fail("should have thrown SyntaxError for JSON.parse on " + str);
+ } catch (e) {
+ if (! (e instanceof SyntaxError)) {
+ fail("Expected SyntaxError, but got " + e);
+ }
+ }
+}
+
+// invalid escape in a string
+checkJSON('"\\a"')
+
+// invalid floating point number patterns
+checkJSON("1.")
+checkJSON(".8")
+checkJSON("2.3e+")
+checkJSON("0.3E+")
+
+// octal, hexadecimal not allowed
+checkJSON("08")
+checkJSON("06")
+checkJSON('0x3')