src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSONFunctions.java

Print this page

        

@@ -23,25 +23,23 @@
  * questions.
  */
 
 package jdk.nashorn.internal.runtime;
 
-import static jdk.nashorn.internal.runtime.Source.sourceFor;
-
 import java.lang.invoke.MethodHandle;
+import java.util.ArrayList;
 import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 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.codegen.ObjectClassGenerator;
 import jdk.nashorn.internal.objects.Global;
 import jdk.nashorn.internal.parser.JSONParser;
-import jdk.nashorn.internal.parser.TokenType;
+import jdk.nashorn.internal.runtime.arrays.ArrayData;
 import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
 import jdk.nashorn.internal.runtime.linker.Bootstrap;
+import jdk.nashorn.internal.scripts.JO;
 
 /**
  * Utilities used by "JSON" object implementation.
  */
 public final class JSONFunctions {

@@ -77,22 +75,21 @@
      * @param reviver  optional value: function that takes two parameters (key, value)
      * @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 Global global = Context.getGlobal();
+        final JSONParser parser  = new JSONParser(str);
+        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);
+        final Object unfiltered = convert(global, Global.objectPrototype(), value);
         return applyReviver(global, unfiltered, reviver);
     }
 
     // -- Internals only below this point
 

@@ -135,62 +132,98 @@
         } catch(final Throwable t) {
             throw new RuntimeException(t);
         }
     }
 
-    // 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];
+    // Converts collections to JS objects
+    @SuppressWarnings("unchecked")
+    private static Object convert(final Global global, final ScriptObject objectProto, final Object value) {
+        if (value instanceof Map) {
+
+            final Map<String, Object> map = (Map) value;
+            final int length = map.size();
+            final List<Property> properties = new ArrayList<>(length);
+            final Object[] objectSpill = new Object[length];
+            final long[] primitiveSpill = new long[length];
+            ArrayData arrayData = ArrayData.EMPTY_ARRAY;
+            int slot = 0;
+
+            for (final Map.Entry<String, Object> entry : map.entrySet()) {
+                final String name = entry.getKey();
+                final Object convertedValue = convert(global, objectProto, entry.getValue());
+                final int index = ArrayIndex.getArrayIndex(name);
+                if (ArrayIndex.isValidArrayIndex(index)) {
+                    // array index key
+                    final long oldLength = arrayData.length();
+                    final long longIndex = ArrayIndex.toLongIndex(index);
+                    if (longIndex > oldLength) {
+                        if (arrayData.canDelete(oldLength, longIndex - 1, false)) {
+                            arrayData = arrayData.delete(oldLength, longIndex - 1);
+                        }
+                    }
+                    arrayData = arrayData.ensure(longIndex).set(index, convertedValue, false);
+                } else {
+                    // ordinary property key
+                    final Class<?> type;
+                    if (ObjectClassGenerator.OBJECT_FIELDS_ONLY) {
+                        objectSpill[slot] = convertedValue;
+                        type = Object.class;
+                    } else {
+                        type = getType(convertedValue);
+                        if (type == Object.class) {
+                            objectSpill[slot] = convertedValue;
+                        } else {
+                            primitiveSpill[slot] = ObjectClassGenerator.pack((Number) convertedValue);
+                        }
+                    }
+                    final Property property = new SpillProperty(name, 0, slot++);
+                    property.setType(type);
+                    properties.add(property);
+                }
+            }
+
+            final ScriptObject result = new JO(PropertyMap.newMap(properties), primitiveSpill, objectSpill);
+            result.setInitialProto(objectProto);
+            result.setArray(arrayData);
+            return result;
+
+        } else if (value instanceof List) {
+
+            final List<Object> list = (List) value;
+            if (isNumericArray(list)) {
+                final double[] values = new double[list.size()];
                     int   index = 0;
 
-                    for (final Node elem : elements) {
-                        values[index++] = JSType.toNumber(convertNode(global, elem));
+                for (final Object obj : list) {
+                    values[index++] = JSType.toNumber(obj);
                     }
                     return global.wrapAsObject(values);
                 }
 
-                final Object[] values = new Object[elements.length];
+            final Object[] values = new Object[list.size()];
                 int   index = 0;
 
-                for (final Node elem : elements) {
-                    values[index++] = convertNode(global, elem);
+            for (final Object elem : list) {
+                values[index++] = convert(global, objectProto, 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 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();
+
+    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 null;
+            return Object.class;
         }
     }
 
     // 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) {

@@ -205,16 +238,15 @@
             // add new property
             sobj.addOwnProperty(name, Property.WRITABLE_ENUMERABLE_CONFIGURABLE, value);
         }
     }
 
-    // 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;
-            }
+    // does the given list represent a numeric array?
+    private static boolean isNumericArray(final List<Object> list) {
+        for (final Object obj : list) {
+            if (!(obj instanceof Number)) {
             return false;
         }
+        }
         return true;
     }
 }