changeset 1177:82d1bb9324cf

8062141: Various performance issues parsing JSON Reviewed-by: lagergren, attila
author hannesw
date Thu, 05 Feb 2015 14:42:14 +0100
parents 8e86c58cbb00
children 2b9af466a49d
files src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/JSONParser.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Lexer.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSONFunctions.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/scripts/JO.java test/examples/json-parser-micro.js test/script/basic/JDK-8062141.js test/script/basic/JDK-8062141.js.EXPECTED test/script/basic/NASHORN-623.js.EXPECTED
diffstat 10 files changed, 952 insertions(+), 415 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java	Thu Feb 05 19:08:00 2015 +0530
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java	Thu Feb 05 14:42:14 2015 +0100
@@ -580,13 +580,15 @@
         } else if (obj instanceof String || obj instanceof ConsString) {
             return new NativeString((CharSequence)obj, this);
         } else if (obj instanceof Object[]) { // extension
-            return new NativeArray((Object[])obj);
+            return new NativeArray(ArrayData.allocate((Object[])obj), this);
         } else if (obj instanceof double[]) { // extension
-            return new NativeArray((double[])obj);
+            return new NativeArray(ArrayData.allocate((double[])obj), this);
         } else if (obj instanceof long[]) {
-            return new NativeArray((long[])obj);
+            return new NativeArray(ArrayData.allocate((long[])obj), this);
         } else if (obj instanceof int[]) {
-            return new NativeArray((int[])obj);
+            return new NativeArray(ArrayData.allocate((int[]) obj), this);
+        } else if (obj instanceof ArrayData) {
+            return new NativeArray((ArrayData) obj, this);
         } else {
             // FIXME: more special cases? Map? List?
             return obj;
@@ -1028,14 +1030,19 @@
     }
 
     // builtin prototype accessors
+
+    /**
+     * Get the builtin Object prototype.
+      * @return the object prototype.
+     */
+    public ScriptObject getObjectPrototype() {
+        return ScriptFunction.getPrototype(builtinObject);
+    }
+
     ScriptObject getFunctionPrototype() {
         return ScriptFunction.getPrototype(builtinFunction);
     }
 
-    ScriptObject getObjectPrototype() {
-        return ScriptFunction.getPrototype(builtinObject);
-    }
-
     ScriptObject getArrayPrototype() {
         return ScriptFunction.getPrototype(builtinArray);
     }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/JSONParser.java	Thu Feb 05 19:08:00 2015 +0530
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/JSONParser.java	Thu Feb 05 14:42:14 2015 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2015, 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
@@ -25,38 +25,59 @@
 
 package jdk.nashorn.internal.parser;
 
-import static jdk.nashorn.internal.parser.TokenType.COLON;
-import static jdk.nashorn.internal.parser.TokenType.COMMARIGHT;
-import static jdk.nashorn.internal.parser.TokenType.EOF;
-import static jdk.nashorn.internal.parser.TokenType.ESCSTRING;
-import static jdk.nashorn.internal.parser.TokenType.RBRACE;
-import static jdk.nashorn.internal.parser.TokenType.RBRACKET;
-import static jdk.nashorn.internal.parser.TokenType.STRING;
 import java.util.ArrayList;
 import java.util.List;
-import jdk.nashorn.internal.ir.Expression;
-import jdk.nashorn.internal.ir.LiteralNode;
-import jdk.nashorn.internal.ir.Node;
-import jdk.nashorn.internal.ir.ObjectNode;
-import jdk.nashorn.internal.ir.PropertyNode;
-import jdk.nashorn.internal.ir.UnaryNode;
+import jdk.nashorn.internal.codegen.ObjectClassGenerator;
+import jdk.nashorn.internal.objects.Global;
+import jdk.nashorn.internal.runtime.ECMAErrors;
 import jdk.nashorn.internal.runtime.ErrorManager;
+import jdk.nashorn.internal.runtime.JSErrorType;
+import jdk.nashorn.internal.runtime.JSType;
+import jdk.nashorn.internal.runtime.ParserException;
+import jdk.nashorn.internal.runtime.Property;
+import jdk.nashorn.internal.runtime.PropertyMap;
+import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.Source;
+import jdk.nashorn.internal.runtime.SpillProperty;
+import jdk.nashorn.internal.runtime.arrays.ArrayData;
+import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
+import jdk.nashorn.internal.scripts.JO;
+
+import static jdk.nashorn.internal.parser.TokenType.STRING;
 
 /**
  * Parses JSON text and returns the corresponding IR node. This is derived from the objectLiteral production of the main parser.
  *
  * See: 15.12.1.2 The JSON Syntactic Grammar
  */
-public class JSONParser extends AbstractParser {
+public class JSONParser {
+
+    final private String source;
+    final private Global global;
+    final int length;
+    int pos = 0;
+
+    private static PropertyMap EMPTY_MAP = PropertyMap.newMap();
+
+    private static final int EOF = -1;
+
+    private static final String TRUE  = "true";
+    private static final String FALSE = "false";
+    private static final String NULL  = "null";
+
+    private static final int STATE_EMPTY          = 0;
+    private static final int STATE_ELEMENT_PARSED = 1;
+    private static final int STATE_COMMA_PARSED   = 2;
 
     /**
      * Constructor
      * @param source  the source
-     * @param errors  the error manager
+     * @param global the global object
      */
-    public JSONParser(final Source source, final ErrorManager errors) {
-        super(source, errors, false, 0);
+    public JSONParser(final String source, final Global global ) {
+        this.source = source;
+        this.global = global;
+        this.length = source.length();
     }
 
     /**
@@ -114,329 +135,408 @@
     }
 
     /**
-     * Public parsed method - start lexing a new token stream for
-     * a JSON script
+     * Public parse method. Parse a string into a JSON object.
      *
-     * @return the JSON literal
+     * @return the parsed JSON Object
      */
-    public Node parse() {
-        stream = new TokenStream();
-
-        lexer = new Lexer(source, stream) {
-
-            @Override
-            protected boolean skipComments() {
-                return false;
-            }
-
-            @Override
-            protected boolean isStringDelimiter(final char ch) {
-                return ch == '\"';
-            }
-
-            // ECMA 15.12.1.1 The JSON Lexical Grammar - JSONWhiteSpace
-            @Override
-            protected boolean isWhitespace(final char ch) {
-                return Lexer.isJsonWhitespace(ch);
-            }
-
-            @Override
-            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 startPosition = position;
-                // Assume value is a decimal.
-                TokenType valueType = 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.
-                final 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);
-                        }
-                    }
-
-                    valueType = TokenType.FLOATING;
-                }
-
-                // Add number token.
-                add(valueType, startPosition);
-            }
-
-            // 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;
-
-        next();
-
-        final Node resultNode = jsonLiteral();
-        expect(EOF);
-
-        return resultNode;
+    public Object parse() {
+        final Object value = parseLiteral();
+        skipWhiteSpace();
+        if (pos < length) {
+            throw expectedError(pos, "eof", toString(peek()));
+        }
+        return value;
     }
 
-    @SuppressWarnings("fallthrough")
-    private LiteralNode<?> getStringLiteral() {
-        final LiteralNode<?> literal = getLiteral();
-        final String         str     = (String)literal.getValue();
+    private Object parseLiteral() {
+        skipWhiteSpace();
 
-        for (int i = 0; i < str.length(); i++) {
-            final char ch = str.charAt(i);
-            switch (ch) {
+        final int c = peek();
+        if (c == EOF) {
+            throw expectedError(pos, "json literal", "eof");
+        }
+        switch (c) {
+        case '{':
+            return parseObject();
+        case '[':
+            return parseArray();
+        case '"':
+            return parseString();
+        case 'f':
+            return parseKeyword(FALSE, Boolean.FALSE);
+        case 't':
+            return parseKeyword(TRUE, Boolean.TRUE);
+        case 'n':
+            return parseKeyword(NULL, null);
+        default:
+            if (isDigit(c) || c == '-') {
+                return parseNumber();
+            } else if (c == '.') {
+                throw numberError(pos);
+            } else {
+                throw expectedError(pos, "json literal", toString(c));
+            }
+        }
+    }
+
+    private Object parseObject() {
+        PropertyMap propertyMap = EMPTY_MAP;
+        ArrayData arrayData = ArrayData.EMPTY_ARRAY;
+        final ArrayList<Object> values = new ArrayList<>();
+        int state = STATE_EMPTY;
+
+        assert peek() == '{';
+        pos++;
+
+        while (pos < length) {
+            skipWhiteSpace();
+            final int c = peek();
+
+            switch (c) {
+            case '"':
+                if (state == STATE_ELEMENT_PARSED) {
+                    throw expectedError(pos - 1, ", or }", toString(c));
+                }
+                final String id = parseString();
+                expectColon();
+                final Object value = parseLiteral();
+                final int index = ArrayIndex.getArrayIndex(id);
+                if (ArrayIndex.isValidArrayIndex(index)) {
+                    arrayData = addArrayElement(arrayData, index, value);
+                } else {
+                    propertyMap = addObjectProperty(propertyMap, values, id, value);
+                }
+                state = STATE_ELEMENT_PARSED;
+                break;
+            case ',':
+                if (state != STATE_ELEMENT_PARSED) {
+                    throw error(AbstractParser.message("trailing.comma.in.json"), pos);
+                }
+                state = STATE_COMMA_PARSED;
+                pos++;
+                break;
+            case '}':
+                if (state == STATE_COMMA_PARSED) {
+                    throw error(AbstractParser.message("trailing.comma.in.json"), pos);
+                }
+                pos++;
+                return createObject(propertyMap, values, arrayData);
             default:
-                if (ch > 0x001f) {
-                    break;
-                }
-            case '"':
-            case '\\':
-                throw error(AbstractParser.message("unexpected.token", str));
+                throw expectedError(pos, ", or }", toString(c));
+            }
+        }
+        throw expectedError(pos, ", or }", "eof");
+    }
+
+    private static ArrayData addArrayElement(final ArrayData arrayData, final int index, final Object value) {
+        final long oldLength = arrayData.length();
+        final long longIndex = ArrayIndex.toLongIndex(index);
+        ArrayData newArrayData = arrayData;
+        if (longIndex > oldLength) {
+            if (arrayData.canDelete(oldLength, longIndex - 1, false)) {
+                newArrayData = newArrayData.delete(oldLength, longIndex - 1);
+            }
+        }
+        return newArrayData.ensure(longIndex).set(index, value, false);
+    }
+
+    private static PropertyMap addObjectProperty(final PropertyMap propertyMap, final List<Object> values,
+                                                 final String id, final Object value) {
+        final Property oldProperty = propertyMap.findProperty(id);
+        final Property newProperty;
+        final PropertyMap newMap;
+        final Class<?> type = ObjectClassGenerator.OBJECT_FIELDS_ONLY ? Object.class : getType(value);
+
+        if (oldProperty != null) {
+            values.set(oldProperty.getSlot(), value);
+            newProperty = new SpillProperty(id, 0, oldProperty.getSlot());
+            newProperty.setType(type);
+            newMap = propertyMap.replaceProperty(oldProperty, newProperty);;
+        } else {
+            values.add(value);
+            newProperty = new SpillProperty(id, 0, propertyMap.size());
+            newProperty.setType(type);
+            newMap = propertyMap.addProperty(newProperty);
+        }
+
+        return newMap;
+    }
+
+    private Object createObject(final PropertyMap propertyMap, final List<Object> values, final ArrayData arrayData) {
+        final long[] primitiveSpill = new long[values.size()];
+        final Object[] objectSpill = new Object[values.size()];
+
+        for (final Property property : propertyMap.getProperties()) {
+            if (property.getType() == Object.class) {
+                objectSpill[property.getSlot()] = values.get(property.getSlot());
+            } else {
+                primitiveSpill[property.getSlot()] = ObjectClassGenerator.pack((Number) values.get(property.getSlot()));
             }
         }
 
-        return literal;
+        final ScriptObject object = new JO(propertyMap, primitiveSpill, objectSpill);
+        object.setInitialProto(global.getObjectPrototype());
+        object.setArray(arrayData);
+        return object;
     }
 
-    /**
-     * Parse a JSON literal from the token stream
-     * @return the JSON literal as a Node
-     */
-    private Expression jsonLiteral() {
-        final long literalToken = token;
-
-        switch (type) {
-        case STRING:
-            return getStringLiteral();
-        case ESCSTRING:
-        case DECIMAL:
-        case FLOATING:
-            return getLiteral();
-        case FALSE:
-            next();
-            return LiteralNode.newInstance(literalToken, finish, false);
-        case TRUE:
-            next();
-            return LiteralNode.newInstance(literalToken, finish, true);
-        case NULL:
-            next();
-            return LiteralNode.newInstance(literalToken, finish);
-        case LBRACKET:
-            return arrayLiteral();
-        case LBRACE:
-            return objectLiteral();
-        /*
-         * A.8.1 JSON Lexical Grammar
-         *
-         * JSONNumber :: See 15.12.1.1
-         *    -opt DecimalIntegerLiteral JSONFractionopt ExponentPartopt
-         */
-        case SUB:
-            next();
-
-            final long realToken = token;
-            final Object value = getValue();
-
-            if (value instanceof Number) {
-                next();
-                return new UnaryNode(literalToken, LiteralNode.newInstance(realToken, finish, (Number)value));
-            }
-
-            throw error(AbstractParser.message("expected", "number", type.getNameOrType()));
-        default:
-            break;
+    private static Class<?> getType(final Object value) {
+        if (value instanceof Integer) {
+            return int.class;
+        } else if (value instanceof Long) {
+            return long.class;
+        } else if (value instanceof Double) {
+            return double.class;
+        } else {
+            return Object.class;
         }
-
-        throw error(AbstractParser.message("expected", "json literal", type.getNameOrType()));
     }
 
-    /**
-     * Parse an array literal from the token stream
-     * @return the array literal as a Node
-     */
-    private LiteralNode<Expression[]> arrayLiteral() {
-        // Unlike JavaScript array literals, elison is not permitted in JSON.
+    private void expectColon() {
+        skipWhiteSpace();
+        final int n = next();
+        if (n != ':') {
+            throw expectedError(pos - 1, ":", toString(n));
+        }
+    }
 
-        // Capture LBRACKET token.
-        final long arrayToken = token;
-        // LBRACKET tested in caller.
-        next();
+    private Object parseArray() {
+        ArrayData arrayData = ArrayData.EMPTY_ARRAY;
+        int state = STATE_EMPTY;
 
-        LiteralNode<Expression[]> result = null;
-        // Prepare to accummulating elements.
-        final List<Expression> elements = new ArrayList<>();
+        assert peek() == '[';
+        pos++;
 
-loop:
-        while (true) {
-            switch (type) {
-            case RBRACKET:
-                next();
-                result = LiteralNode.newInstance(arrayToken, finish, elements);
-                break loop;
+        while (pos < length) {
+            skipWhiteSpace();
+            final int c = peek();
 
-            case COMMARIGHT:
-                next();
-                // check for trailing comma - not allowed in JSON
-                if (type == RBRACKET) {
-                    throw error(AbstractParser.message("trailing.comma.in.json", type.getNameOrType()));
+            switch (c) {
+            case ',':
+                if (state != STATE_ELEMENT_PARSED) {
+                    throw error(AbstractParser.message("trailing.comma.in.json"), pos);
                 }
+                state = STATE_COMMA_PARSED;
+                pos++;
                 break;
-
+            case ']':
+                if (state == STATE_COMMA_PARSED) {
+                    throw error(AbstractParser.message("trailing.comma.in.json"), pos);
+                }
+                pos++;
+                return global.wrapAsObject(arrayData);
             default:
-                // Add expression element.
-                elements.add(jsonLiteral());
-                // Comma between array elements is mandatory in JSON.
-                if (type != COMMARIGHT && type != RBRACKET) {
-                   throw error(AbstractParser.message("expected", ", or ]", type.getNameOrType()));
+                if (state == STATE_ELEMENT_PARSED) {
+                    throw expectedError(pos, ", or ]", toString(c));
                 }
+                final long index = arrayData.length();
+                arrayData = arrayData.ensure(index).set((int) index, parseLiteral(), true);
+                state = STATE_ELEMENT_PARSED;
                 break;
             }
         }
 
-        return result;
+        throw expectedError(pos, ", or ]", "eof");
     }
 
-    /**
-     * Parse an object literal from the token stream
-     * @return the object literal as a Node
-     */
-    private ObjectNode objectLiteral() {
-        // Capture LBRACE token.
-        final long objectToken = token;
-        // LBRACE tested in caller.
-        next();
+    private String parseString() {
+        // String buffer is only instantiated if string contains escape sequences.
+        int start = ++pos;
+        StringBuilder sb = null;
 
-        // Prepare to accumulate elements.
-        final List<PropertyNode> elements = new ArrayList<>();
+        while (pos < length) {
+            final int c = next();
+            if (c <= 0x1f) {
+                // Characters < 0x1f are not allowed in JSON strings.
+                throw syntaxError(pos, "String contains control character");
 
-        // Create a block for the object literal.
-loop:
-        while (true) {
-            switch (type) {
-            case RBRACE:
-                next();
-                break loop;
+            } else if (c == '\\') {
+                if (sb == null) {
+                    sb = new StringBuilder(pos - start + 16);
+                }
+                sb.append(source, start, pos - 1);
+                sb.append(parseEscapeSequence());
+                start = pos;
 
-            case COMMARIGHT:
-                next();
-                // check for trailing comma - not allowed in JSON
-                if (type == RBRACE) {
-                    throw error(AbstractParser.message("trailing.comma.in.json", type.getNameOrType()));
+            } else if (c == '"') {
+                if (sb != null) {
+                    sb.append(source, start, pos - 1);
+                    return sb.toString();
                 }
-                break;
-
-            default:
-                // Get and add the next property.
-                final PropertyNode property = propertyAssignment();
-                elements.add(property);
-
-                // Comma between property assigments is mandatory in JSON.
-                if (type != RBRACE && type != COMMARIGHT) {
-                    throw error(AbstractParser.message("expected", ", or }", type.getNameOrType()));
-                }
-                break;
+                return source.substring(start, pos - 1);
             }
         }
 
-        // Construct new object literal.
-        return new ObjectNode(objectToken, finish, elements);
+        throw error(Lexer.message("missing.close.quote"), pos, length);
     }
 
-    /**
-     * Parse a property assignment from the token stream
-     * @return the property assignment as a Node
-     */
-    private PropertyNode propertyAssignment() {
-        // Capture firstToken.
-        final long propertyToken = token;
-        LiteralNode<?> name = null;
+    private char parseEscapeSequence() {
+        final int c = next();
+        switch (c) {
+        case '"':
+            return '"';
+        case '\\':
+            return '\\';
+        case '/':
+            return '/';
+        case 'b':
+            return '\b';
+        case 'f':
+            return '\f';
+        case 'n':
+            return '\n';
+        case 'r':
+            return '\r';
+        case 't':
+            return '\t';
+        case 'u':
+            return parseUnicodeEscape();
+        default:
+            throw error(Lexer.message("invalid.escape.char"), pos - 1, length);
+        }
+    }
 
-        if (type == STRING) {
-            name = getStringLiteral();
-        } else if (type == ESCSTRING) {
-            name = getLiteral();
+    private char parseUnicodeEscape() {
+        return (char) (parseHexDigit() << 12 | parseHexDigit() << 8 | parseHexDigit() << 4 | parseHexDigit());
+    }
+
+    private int parseHexDigit() {
+        final int c = next();
+        if (c >= '0' && c <= '9') {
+            return c - '0';
+        } else if (c >= 'A' && c <= 'F') {
+            return c + 10 - 'A';
+        } else if (c >= 'a' && c <= 'f') {
+            return c + 10 - 'a';
+        }
+        throw error(Lexer.message("invalid.hex"), pos - 1, length);
+    }
+
+    private boolean isDigit(final int c) {
+        return c >= '0' && c <= '9';
+    }
+
+    private void skipDigits() {
+        while (pos < length) {
+            final int c = peek();
+            if (!isDigit(c)) {
+                break;
+            }
+            pos++;
+        }
+    }
+
+    private Number parseNumber() {
+        final int start = pos;
+        int c = next();
+
+        if (c == '-') {
+            c = next();
+        }
+        if (!isDigit(c)) {
+            throw numberError(start);
+        }
+        // no more digits allowed after 0
+        if (c != '0') {
+            skipDigits();
         }
 
-        if (name != null) {
-            expect(COLON);
-            final Expression value = jsonLiteral();
-            return new PropertyNode(propertyToken, value.getFinish(), name, value, null, null);
+        // fraction
+        if (peek() == '.') {
+            pos++;
+            if (!isDigit(next())) {
+                throw numberError(pos - 1);
+            }
+            skipDigits();
         }
 
-        // Raise an error.
-        throw error(AbstractParser.message("expected", "string", type.getNameOrType()));
+        // exponent
+        c = peek();
+        if (c == 'e' || c == 'E') {
+            pos++;
+            c = next();
+            if (c == '-' || c == '+') {
+                c = next();
+            }
+            if (!isDigit(c)) {
+                throw numberError(pos - 1);
+            }
+            skipDigits();
+        }
+
+        final double d = Double.parseDouble(source.substring(start, pos));
+        if (JSType.isRepresentableAsInt(d)) {
+            return (int) d;
+        } else if (JSType.isRepresentableAsLong(d)) {
+            return (long) d;
+        }
+        return d;
     }
 
+    private Object parseKeyword(final String keyword, final Object value) {
+        if (!source.regionMatches(pos, keyword, 0, keyword.length())) {
+            throw expectedError(pos, "json literal", "ident");
+        }
+        pos += keyword.length();
+        return value;
+    }
+
+    private int peek() {
+        if (pos >= length) {
+            return -1;
+        }
+        return source.charAt(pos);
+    }
+
+    private int next() {
+        final int next = peek();
+        pos++;
+        return next;
+    }
+
+    private void skipWhiteSpace() {
+        while (pos < length) {
+            switch (peek()) {
+            case '\t':
+            case '\r':
+            case '\n':
+            case ' ':
+                pos++;
+                break;
+            default:
+                return;
+            }
+        }
+    }
+
+    private static String toString(final int c) {
+        return c == EOF ? "eof" : String.valueOf((char) c);
+    }
+
+    ParserException error(final String message, final int start, final int length) throws ParserException {
+        final long token     = Token.toDesc(STRING, start, length);
+        final int  pos       = Token.descPosition(token);
+        final Source src     = Source.sourceFor("<json>", source);
+        final int  lineNum   = src.getLine(pos);
+        final int  columnNum = src.getColumn(pos);
+        final String formatted = ErrorManager.format(message, src, lineNum, columnNum, token);
+        return new ParserException(JSErrorType.SYNTAX_ERROR, formatted, src, lineNum, columnNum, token);
+    }
+
+    private ParserException error(final String message, final int start) {
+        return error(message, start, length);
+    }
+
+    private ParserException numberError(final int start) {
+        return error(Lexer.message("json.invalid.number"), start);
+    }
+
+    private ParserException expectedError(final int start, final String expected, final String found) {
+        return error(AbstractParser.message("expected", expected, found), start);
+    }
+
+    private ParserException syntaxError(final int start, final String reason) {
+        final String message = ECMAErrors.getMessage("syntax.error.invalid.json", reason);
+        return error(message, start);
+    }
 }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Lexer.java	Thu Feb 05 19:08:00 2015 +0530
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Lexer.java	Thu Feb 05 14:42:14 2015 +0100
@@ -93,9 +93,6 @@
     private static final String SPACETAB = " \t";  // ASCII space and tab
     private static final String LFCR     = "\n\r"; // line feed and carriage return (ctrl-m)
 
-    private static final String JSON_WHITESPACE_EOL = LFCR;
-    private static final String JSON_WHITESPACE     = SPACETAB + LFCR;
-
     private static final String JAVASCRIPT_WHITESPACE_EOL =
         LFCR +
         "\u2028" + // line separator
@@ -385,24 +382,6 @@
     }
 
     /**
-     * Test whether a char is valid JSON whitespace
-     * @param ch a char
-     * @return true if valid JSON whitespace
-     */
-    public static boolean isJsonWhitespace(final char ch) {
-        return JSON_WHITESPACE.indexOf(ch) != -1;
-    }
-
-    /**
-     * Test whether a char is valid JSON end of line
-     * @param ch a char
-     * @return true if valid JSON end of line
-     */
-    public static boolean isJsonEOL(final char ch) {
-        return JSON_WHITESPACE_EOL.indexOf(ch) != -1;
-    }
-
-    /**
      * Test if char is a string delimiter, e.g. '\' or '"'.  Also scans exec
      * strings ('`') in scripting mode.
      * @param ch a char
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSONFunctions.java	Thu Feb 05 19:08:00 2015 +0530
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSONFunctions.java	Thu Feb 05 14:42:14 2015 +0100
@@ -25,19 +25,11 @@
 
 package jdk.nashorn.internal.runtime;
 
-import static jdk.nashorn.internal.runtime.Source.sourceFor;
-
 import java.lang.invoke.MethodHandle;
 import java.util.Iterator;
 import java.util.concurrent.Callable;
-import jdk.nashorn.internal.ir.LiteralNode;
-import jdk.nashorn.internal.ir.Node;
-import jdk.nashorn.internal.ir.ObjectNode;
-import jdk.nashorn.internal.ir.PropertyNode;
-import jdk.nashorn.internal.ir.UnaryNode;
 import jdk.nashorn.internal.objects.Global;
 import jdk.nashorn.internal.parser.JSONParser;
-import jdk.nashorn.internal.parser.TokenType;
 import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
 import jdk.nashorn.internal.runtime.linker.Bootstrap;
 
@@ -78,20 +70,18 @@
      * @return Object representation of JSON text given
      */
     public static Object parse(final Object text, final Object reviver) {
-        final String     str     = JSType.toString(text);
-        final JSONParser parser  = new JSONParser(sourceFor("<json>", str), new Context.ThrowErrorManager());
-
-        Node node;
+        final String     str    = JSType.toString(text);
+        final Global     global = Context.getGlobal();
+        final JSONParser parser = new JSONParser(str, global);
+        final Object     value;
 
         try {
-            node = parser.parse();
+            value = parser.parse();
         } catch (final ParserException e) {
             throw ECMAErrors.syntaxError(e, "invalid.json", e.getMessage());
         }
 
-        final Global global = Context.getGlobal();
-        final Object unfiltered = convertNode(global, node);
-        return applyReviver(global, unfiltered, reviver);
+        return applyReviver(global, value, reviver);
     }
 
     // -- Internals only below this point
@@ -137,61 +127,6 @@
         }
     }
 
-    // Converts IR node to runtime value
-    private static Object convertNode(final Global global, final Node node) {
-        if (node instanceof LiteralNode) {
-            // check for array literal
-            if (node.tokenType() == TokenType.ARRAY) {
-                assert node instanceof LiteralNode.ArrayLiteralNode;
-                final Node[] elements = ((LiteralNode.ArrayLiteralNode)node).getValue();
-
-                // NOTE: We cannot use LiteralNode.isNumericArray() here as that
-                // method uses symbols of element nodes. Since we don't do lower
-                // pass, there won't be any symbols!
-                if (isNumericArray(elements)) {
-                    final double[] values = new double[elements.length];
-                    int   index = 0;
-
-                    for (final Node elem : elements) {
-                        values[index++] = JSType.toNumber(convertNode(global, elem));
-                    }
-                    return global.wrapAsObject(values);
-                }
-
-                final Object[] values = new Object[elements.length];
-                int   index = 0;
-
-                for (final Node elem : elements) {
-                    values[index++] = convertNode(global, elem);
-                }
-
-                return global.wrapAsObject(values);
-            }
-
-            return ((LiteralNode<?>)node).getValue();
-
-        } else if (node instanceof ObjectNode) {
-            final ObjectNode   objNode  = (ObjectNode) node;
-            final ScriptObject object   = global.newObject();
-
-            for (final PropertyNode pNode: objNode.getElements()) {
-                final Node         valueNode = pNode.getValue();
-
-                final String name = pNode.getKeyName();
-                final Object value = convertNode(global, valueNode);
-                setPropertyValue(object, name, value);
-            }
-
-            return object;
-        } else if (node instanceof UnaryNode) {
-            // UnaryNode used only to represent negative number JSON value
-            final UnaryNode unaryNode = (UnaryNode)node;
-            return -((LiteralNode<?>)unaryNode.getExpression()).getNumber();
-        } else {
-            return null;
-        }
-    }
-
     // add a new property if does not exist already, or else set old property
     private static void setPropertyValue(final ScriptObject sobj, final String name, final Object value) {
         final int index = ArrayIndex.getArrayIndex(name);
@@ -207,14 +142,4 @@
         }
     }
 
-    // does the given IR node represent a numeric array?
-    private static boolean isNumericArray(final Node[] values) {
-        for (final Node node : values) {
-            if (node instanceof LiteralNode && ((LiteralNode<?>)node).getValue() instanceof Number) {
-                continue;
-            }
-            return false;
-        }
-        return true;
-    }
 }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java	Thu Feb 05 19:08:00 2015 +0530
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java	Thu Feb 05 14:42:14 2015 +0100
@@ -486,7 +486,7 @@
      *
      * @return New {@link PropertyMap} with {@link Property} replaced.
      */
-    PropertyMap replaceProperty(final Property oldProperty, final Property newProperty) {
+    public PropertyMap replaceProperty(final Property oldProperty, final Property newProperty) {
         if (listeners != null) {
             listeners.propertyModified(oldProperty, newProperty);
         }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/scripts/JO.java	Thu Feb 05 19:08:00 2015 +0530
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/scripts/JO.java	Thu Feb 05 14:42:14 2015 +0100
@@ -25,7 +25,6 @@
 
 package jdk.nashorn.internal.scripts;
 
-import jdk.nashorn.internal.codegen.SpillObjectCreator;
 import jdk.nashorn.internal.runtime.PropertyMap;
 import jdk.nashorn.internal.runtime.ScriptObject;
 
@@ -64,8 +63,9 @@
     }
 
     /**
-     * Constructor that takes a pre-initialized spill pool. Used for
-     * by {@link SpillObjectCreator} for intializing object literals
+     * Constructor that takes a pre-initialized spill pool. Used by
+     * {@link jdk.nashorn.internal.codegen.SpillObjectCreator} and
+     * {@link jdk.nashorn.internal.parser.JSONParser} for initializing object literals
      *
      * @param map            property map
      * @param primitiveSpill primitive spill pool
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/examples/json-parser-micro.js	Thu Feb 05 14:42:14 2015 +0100
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   - Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *   - Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ *   - Neither the name of Oracle nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+function bench() {
+    var start = Date.now();
+    for (var i = 0; i < 2000; i++) {
+        JSON.parse(String(json));
+    }
+    print("1000 iterations in", Date.now() - start, "millis");
+}
+
+var json = '[\
+  {\
+    "_id": "54ca34171d3ade49782294c8",\
+    "index": 0,\
+    "guid": "ed0e74d5-ac63-47b6-8938-1750abab5770",\
+    "isActive": false,\
+    "balance": "$1,996.19",\
+    "picture": "http://placehold.it/32x32",\
+    "age": 39,\
+    "eyeColor": "green",\
+    "name": "Rose Graham",\
+    "gender": "male",\
+    "company": "PRIMORDIA",\
+    "email": "rosegraham@primordia.com",\
+    "phone": "+1 (985) 600-3551",\
+    "address": "364 Melba Court, Succasunna, Texas, 8393",\
+    "about": "Sunt commodo cillum occaecat velit eu eiusmod ex eiusmod sunt deserunt nulla proident incididunt. Incididunt ullamco Lorem elit do culpa esse do ex dolor aliquip labore. Ullamco velit laboris incididunt dolor. Nostrud dolor sint pariatur fugiat ullamco exercitation. Eu laboris do cupidatat eiusmod incididunt mollit occaecat voluptate.",\
+    "registered": "2014-03-13T12:05:14 -01:00",\
+    "latitude": 18.55665,\
+    "longitude": 81.641001,\
+    "tags": [\
+      "sint",\
+      "Lorem",\
+      "veniam",\
+      "quis",\
+      "proident",\
+      "consectetur",\
+      "consequat"\
+    ],\
+    "friends": [\
+      {\
+        "id": 0,\
+        "name": "Evangelina Morgan"\
+      },\
+      {\
+        "id": 1,\
+        "name": "Saunders Snyder"\
+      },\
+      {\
+        "id": 2,\
+        "name": "Walker Wood"\
+      }\
+    ],\
+    "greeting": "Hello, Rose Graham! You have 1 unread messages.",\
+    "favoriteFruit": "strawberry"\
+  },\
+  {\
+    "_id": "54ca34176790c4c60fcae085",\
+    "index": 1,\
+    "guid": "9dc42e4c-b58f-4d92-a2ee-968d2b627d92",\
+    "isActive": true,\
+    "balance": "$3,832.97",\
+    "picture": "http://placehold.it/32x32",\
+    "age": 40,\
+    "eyeColor": "brown",\
+    "name": "Delaney Cherry",\
+    "gender": "male",\
+    "company": "INJOY",\
+    "email": "delaneycherry@injoy.com",\
+    "phone": "+1 (807) 463-2295",\
+    "address": "470 Hale Avenue, Mulberry, District Of Columbia, 5455",\
+    "about": "Deserunt sit cupidatat elit Lorem excepteur ex. Magna officia minim cupidatat nulla enim deserunt. Amet ex in tempor commodo consequat non ad qui elit cupidatat esse labore sint.",\
+    "registered": "2014-03-27T23:06:33 -01:00",\
+    "latitude": -4.984238,\
+    "longitude": 116.039285,\
+    "tags": [\
+      "minim",\
+      "velit",\
+      "aute",\
+      "minim",\
+      "id",\
+      "enim",\
+      "enim"\
+    ],\
+    "friends": [\
+      {\
+        "id": 0,\
+        "name": "Barrera Flowers"\
+      },\
+      {\
+        "id": 1,\
+        "name": "Leann Larson"\
+      },\
+      {\
+        "id": 2,\
+        "name": "Latoya Petty"\
+      }\
+    ],\
+    "greeting": "Hello, Delaney Cherry! You have 2 unread messages.",\
+    "favoriteFruit": "strawberry"\
+  },\
+  {\
+    "_id": "54ca3417920666f00c54bfc4",\
+    "index": 2,\
+    "guid": "f91e08f8-1598-49bc-a08b-bb48f0cc1751",\
+    "isActive": true,\
+    "balance": "$2,932.84",\
+    "picture": "http://placehold.it/32x32",\
+    "age": 28,\
+    "eyeColor": "brown",\
+    "name": "Mosley Hammond",\
+    "gender": "male",\
+    "company": "AQUACINE",\
+    "email": "mosleyhammond@aquacine.com",\
+    "phone": "+1 (836) 598-2591",\
+    "address": "879 Columbia Place, Seymour, Montana, 4897",\
+    "about": "Sunt laborum incididunt et elit in deserunt deserunt irure enim ea qui non. Minim nisi sint aute veniam reprehenderit veniam reprehenderit. Elit enim eu voluptate eu cupidatat nulla ea incididunt exercitation voluptate ut aliquip excepteur ipsum. Consequat anim fugiat irure Lorem anim consectetur est.",\
+    "registered": "2014-07-27T05:05:58 -02:00",\
+    "latitude": -43.608015,\
+    "longitude": -38.33894,\
+    "tags": [\
+      "proident",\
+      "incididunt",\
+      "eiusmod",\
+      "anim",\
+      "consectetur",\
+      "qui",\
+      "excepteur"\
+    ],\
+    "friends": [\
+      {\
+        "id": 0,\
+        "name": "Hanson Davidson"\
+      },\
+      {\
+        "id": 1,\
+        "name": "Autumn Kaufman"\
+      },\
+      {\
+        "id": 2,\
+        "name": "Tammy Foley"\
+      }\
+    ],\
+    "greeting": "Hello, Mosley Hammond! You have 4 unread messages.",\
+    "favoriteFruit": "apple"\
+  },\
+  {\
+    "_id": "54ca341753b67572a2b04935",\
+    "index": 3,\
+    "guid": "3377416b-43a2-4f9e-ada3-2479e13b44b8",\
+    "isActive": false,\
+    "balance": "$3,821.54",\
+    "picture": "http://placehold.it/32x32",\
+    "age": 31,\
+    "eyeColor": "green",\
+    "name": "Mueller Barrett",\
+    "gender": "male",\
+    "company": "GROK",\
+    "email": "muellerbarrett@grok.com",\
+    "phone": "+1 (890) 535-2834",\
+    "address": "571 Norwood Avenue, Westwood, Arkansas, 2164",\
+    "about": "Occaecat est sunt commodo ut ex excepteur elit nulla velit minim commodo commodo esse. Lorem quis eu minim consectetur. Cupidatat cupidatat consequat sit eu ex non quis nulla veniam sint enim excepteur. Consequat minim duis do do minim fugiat minim elit laborum ut velit. Occaecat laboris veniam sint reprehenderit.",\
+    "registered": "2014-07-18T17:15:35 -02:00",\
+    "latitude": 10.746577,\
+    "longitude": -160.266041,\
+    "tags": [\
+      "reprehenderit",\
+      "veniam",\
+      "sint",\
+      "commodo",\
+      "exercitation",\
+      "cillum",\
+      "sunt"\
+    ],\
+    "friends": [\
+      {\
+        "id": 0,\
+        "name": "Summers Finch"\
+      },\
+      {\
+        "id": 1,\
+        "name": "Tracie Mcdaniel"\
+      },\
+      {\
+        "id": 2,\
+        "name": "Ayers Patrick"\
+      }\
+    ],\
+    "greeting": "Hello, Mueller Barrett! You have 7 unread messages.",\
+    "favoriteFruit": "apple"\
+  },\
+  {\
+    "_id": "54ca34172775ab9615db0d1d",\
+    "index": 4,\
+    "guid": "a3102a3e-3f08-4df3-b5b5-62eff985d5ca",\
+    "isActive": true,\
+    "balance": "$3,962.27",\
+    "picture": "http://placehold.it/32x32",\
+    "age": 34,\
+    "eyeColor": "green",\
+    "name": "Patrick Foster",\
+    "gender": "male",\
+    "company": "QUAREX",\
+    "email": "patrickfoster@quarex.com",\
+    "phone": "+1 (805) 577-2362",\
+    "address": "640 Richards Street, Roberts, American Samoa, 5530",\
+    "about": "Aute occaecat occaecat ad eiusmod esse aliqua ullamco minim. Exercitation aute ut ex nostrud deserunt laboris officia amet enim do. Cillum officia laborum occaecat eiusmod reprehenderit ex et aliqua minim elit ex aliqua mollit. Occaecat dolor in fugiat laboris aliquip nisi ad voluptate duis eiusmod ad do.",\
+    "registered": "2014-07-22T16:45:35 -02:00",\
+    "latitude": 6.609025,\
+    "longitude": -5.357026,\
+    "tags": [\
+      "ea",\
+      "ut",\
+      "excepteur",\
+      "enim",\
+      "ad",\
+      "non",\
+      "sit"\
+    ],\
+    "friends": [\
+      {\
+        "id": 0,\
+        "name": "Duncan Lewis"\
+      },\
+      {\
+        "id": 1,\
+        "name": "Alyce Benton"\
+      },\
+      {\
+        "id": 2,\
+        "name": "Angelique Larsen"\
+      }\
+    ],\
+    "greeting": "Hello, Patrick Foster! You have 1 unread messages.",\
+    "favoriteFruit": "strawberry"\
+  },\
+  {\
+    "_id": "54ca3417a190f26fef815f6d",\
+    "index": 5,\
+    "guid": "c09663dd-bb0e-45a4-960c-232c0e8a9486",\
+    "isActive": false,\
+    "balance": "$1,871.12",\
+    "picture": "http://placehold.it/32x32",\
+    "age": 20,\
+    "eyeColor": "blue",\
+    "name": "Foreman Chaney",\
+    "gender": "male",\
+    "company": "DEMINIMUM",\
+    "email": "foremanchaney@deminimum.com",\
+    "phone": "+1 (966) 523-2182",\
+    "address": "960 Granite Street, Sunnyside, Tennessee, 1097",\
+    "about": "Adipisicing nisi qui id sit incididunt aute exercitation veniam consequat ipsum sit irure. Aute officia commodo Lorem consequat. Labore exercitation consequat voluptate deserunt consequat do est fugiat nisi eu dolor minim id ea.",\
+    "registered": "2015-01-21T00:18:00 -01:00",\
+    "latitude": -69.841726,\
+    "longitude": 121.809383,\
+    "tags": [\
+      "laboris",\
+      "sunt",\
+      "exercitation",\
+      "enim",\
+      "anim",\
+      "excepteur",\
+      "tempor"\
+    ],\
+    "friends": [\
+      {\
+        "id": 0,\
+        "name": "Espinoza Johnston"\
+      },\
+      {\
+        "id": 1,\
+        "name": "Doreen Holder"\
+      },\
+      {\
+        "id": 2,\
+        "name": "William Ellison"\
+      }\
+    ],\
+    "greeting": "Hello, Foreman Chaney! You have 5 unread messages.",\
+    "favoriteFruit": "strawberry"\
+  }\
+]';
+
+for (var i = 0; i < 100; i++) {
+    bench();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/JDK-8062141.js	Thu Feb 05 14:42:14 2015 +0100
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2015, 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-8062141: Various performance issues parsing JSON
+ *
+ * @test
+ * @run
+ */
+
+function testJson(json) {
+    try {
+        print(JSON.stringify(JSON.parse(json)));
+    } catch (error) {
+        print(error);
+    }
+}
+
+testJson('"\\u003f"');
+testJson('"\\u0"');
+testJson('"\\u0"');
+testJson('"\\u00"');
+testJson('"\\u003"');
+testJson('"\\u003x"');
+testJson('"\\"');
+testJson('"');
+testJson('+1');
+testJson('-1');
+testJson('1.');
+testJson('.1');
+testJson('01');
+testJson('1e');
+testJson('1e0');
+testJson('1a');
+testJson('1e+');
+testJson('1e-');
+testJson('0.0e+0');
+testJson('0.0e-0');
+testJson('[]');
+testJson('[ 1 ]');
+testJson('[1,]');
+testJson('[ 1 , 2 ]');
+testJson('[1, 2');
+testJson('{}');
+testJson('{ "a" : "b" }');
+testJson('{ "a" : "b" ');
+testJson('{ "a" : }');
+testJson('true');
+testJson('tru');
+testJson('true1');
+testJson('false');
+testJson('fals');
+testJson('falser');
+testJson('null');
+testJson('nul');
+testJson('null0');
+testJson('{} 0');
+testJson('{} a');
+testJson('[] 0');
+testJson('[] a');
+testJson('1 0');
+testJson('1 a');
+testJson('["a":true]');
+testJson('{"a",truer}');
+testJson('{"a":truer}');
+testJson('[1, 2, 3]');
+testJson('[9223372036854774000, 9223372036854775000, 9223372036854776000]');
+testJson('[1.1, 1.2, 1.3]');
+testJson('[1, 1.2, 9223372036854776000, null, true]');
+testJson('{ "a" : "string" , "b": 1 , "c" : 1.2 , "d" : 9223372036854776000 , "e" : null , "f" : true }');
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/JDK-8062141.js.EXPECTED	Thu Feb 05 14:42:14 2015 +0100
@@ -0,0 +1,120 @@
+"?"
+SyntaxError: Invalid JSON: <json>:1:4 Invalid hex digit
+"\u0"
+    ^
+SyntaxError: Invalid JSON: <json>:1:4 Invalid hex digit
+"\u0"
+    ^
+SyntaxError: Invalid JSON: <json>:1:5 Invalid hex digit
+"\u00"
+     ^
+SyntaxError: Invalid JSON: <json>:1:6 Invalid hex digit
+"\u003"
+      ^
+SyntaxError: Invalid JSON: <json>:1:6 Invalid hex digit
+"\u003x"
+      ^
+SyntaxError: Invalid JSON: <json>:1:3 Missing close quote
+"\"
+   ^
+SyntaxError: Invalid JSON: <json>:1:1 Missing close quote
+"
+ ^
+SyntaxError: Invalid JSON: <json>:1:0 Expected json literal but found +
++1
+^
+-1
+SyntaxError: Invalid JSON: <json>:1:2 Invalid JSON number format
+1.
+  ^
+SyntaxError: Invalid JSON: <json>:1:0 Invalid JSON number format
+.1
+^
+SyntaxError: Invalid JSON: <json>:1:1 Expected eof but found 1
+01
+ ^
+SyntaxError: Invalid JSON: <json>:1:2 Invalid JSON number format
+1e
+  ^
+1
+SyntaxError: Invalid JSON: <json>:1:1 Expected eof but found a
+1a
+ ^
+SyntaxError: Invalid JSON: <json>:1:3 Invalid JSON number format
+1e+
+   ^
+SyntaxError: Invalid JSON: <json>:1:3 Invalid JSON number format
+1e-
+   ^
+0
+0
+[]
+[1]
+SyntaxError: Invalid JSON: <json>:1:3 Trailing comma is not allowed in JSON
+[1,]
+   ^
+[1,2]
+SyntaxError: Invalid JSON: <json>:1:5 Expected , or ] but found eof
+[1, 2
+     ^
+{}
+{"a":"b"}
+SyntaxError: Invalid JSON: <json>:1:12 Expected , or } but found eof
+{ "a" : "b" 
+            ^
+SyntaxError: Invalid JSON: <json>:1:8 Expected json literal but found }
+{ "a" : }
+        ^
+true
+SyntaxError: Invalid JSON: <json>:1:0 Expected json literal but found ident
+tru
+^
+SyntaxError: Invalid JSON: <json>:1:4 Expected eof but found 1
+true1
+    ^
+false
+SyntaxError: Invalid JSON: <json>:1:0 Expected json literal but found ident
+fals
+^
+SyntaxError: Invalid JSON: <json>:1:5 Expected eof but found r
+falser
+     ^
+null
+SyntaxError: Invalid JSON: <json>:1:0 Expected json literal but found ident
+nul
+^
+SyntaxError: Invalid JSON: <json>:1:4 Expected eof but found 0
+null0
+    ^
+SyntaxError: Invalid JSON: <json>:1:3 Expected eof but found 0
+{} 0
+   ^
+SyntaxError: Invalid JSON: <json>:1:3 Expected eof but found a
+{} a
+   ^
+SyntaxError: Invalid JSON: <json>:1:3 Expected eof but found 0
+[] 0
+   ^
+SyntaxError: Invalid JSON: <json>:1:3 Expected eof but found a
+[] a
+   ^
+SyntaxError: Invalid JSON: <json>:1:2 Expected eof but found 0
+1 0
+  ^
+SyntaxError: Invalid JSON: <json>:1:2 Expected eof but found a
+1 a
+  ^
+SyntaxError: Invalid JSON: <json>:1:4 Expected , or ] but found :
+["a":true]
+    ^
+SyntaxError: Invalid JSON: <json>:1:4 Expected : but found ,
+{"a",truer}
+    ^
+SyntaxError: Invalid JSON: <json>:1:9 Expected , or } but found r
+{"a":truer}
+         ^
+[1,2,3]
+[9223372036854773800,9223372036854774800,9223372036854776000]
+[1.1,1.2,1.3]
+[1,1.2,9223372036854776000,null,true]
+{"a":"string","b":1,"c":1.2,"d":9223372036854776000,"e":null,"f":true}
--- a/test/script/basic/NASHORN-623.js.EXPECTED	Thu Feb 05 19:08:00 2015 +0530
+++ b/test/script/basic/NASHORN-623.js.EXPECTED	Thu Feb 05 14:42:14 2015 +0100
@@ -1,3 +1,3 @@
-SyntaxError: Invalid JSON: <json>:1:12 Expected number but found ident
+SyntaxError: Invalid JSON: <json>:1:11 Invalid JSON number format
 { "test" : -xxx }
-            ^
+           ^