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;
}
}