19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.nashorn.internal.runtime;
27
28 import java.lang.invoke.MethodHandle;
29 import java.util.Iterator;
30 import java.util.List;
31 import jdk.nashorn.internal.ir.LiteralNode;
32 import jdk.nashorn.internal.ir.Node;
33 import jdk.nashorn.internal.ir.ObjectNode;
34 import jdk.nashorn.internal.ir.PropertyNode;
35 import jdk.nashorn.internal.ir.UnaryNode;
36 import jdk.nashorn.internal.parser.JSONParser;
37 import jdk.nashorn.internal.parser.TokenType;
38 import jdk.nashorn.internal.runtime.linker.Bootstrap;
39
40 /**
41 * Utilities used by "JSON" object implementation.
42 */
43 public final class JSONFunctions {
44 private JSONFunctions() {}
45 private static final MethodHandle REVIVER_INVOKER = Bootstrap.createDynamicInvoker("dyn:call", Object.class,
46 ScriptFunction.class, ScriptObject.class, String.class, Object.class);
47
48 /**
49 * Returns JSON-compatible quoted version of the given string.
50 *
51 * @param str String to be quoted
52 * @return JSON-compatible quoted string
53 */
54 public static String quote(final String str) {
55 return JSONParser.quote(str);
56 }
57
58 /**
77 try {
78 node = parser.parse();
79 } catch (final ParserException e) {
80 throw ECMAErrors.syntaxError(e, "invalid.json", e.getMessage());
81 }
82
83 final ScriptObject global = Context.getGlobalTrusted();
84 Object unfiltered = convertNode(global, node);
85 return applyReviver(global, unfiltered, reviver);
86 }
87
88 // -- Internals only below this point
89
90 // parse helpers
91
92 // apply 'reviver' function if available
93 private static Object applyReviver(final ScriptObject global, final Object unfiltered, final Object reviver) {
94 if (reviver instanceof ScriptFunction) {
95 assert global instanceof GlobalObject;
96 final ScriptObject root = ((GlobalObject)global).newObject();
97 root.set("", unfiltered, root.isStrictContext());
98 return walk(root, "", (ScriptFunction)reviver);
99 }
100 return unfiltered;
101 }
102
103 // This is the abstract "Walk" operation from the spec.
104 private static Object walk(final ScriptObject holder, final Object name, final ScriptFunction reviver) {
105 final Object val = holder.get(name);
106 if (val instanceof ScriptObject) {
107 final ScriptObject valueObj = (ScriptObject)val;
108 final boolean strict = valueObj.isStrictContext();
109 final Iterator<String> iter = valueObj.propertyIterator();
110
111 while (iter.hasNext()) {
112 final String key = iter.next();
113 final Object newElement = walk(valueObj, key, reviver);
114
115 if (newElement == ScriptRuntime.UNDEFINED) {
116 valueObj.delete(key, strict);
117 } else {
118 valueObj.set(key, newElement, strict);
119 }
120 }
121 }
122
123 try {
124 // Object.class, ScriptFunction.class, ScriptObject.class, String.class, Object.class);
125 return REVIVER_INVOKER.invokeExact(reviver, holder, JSType.toString(name), val);
126 } catch(Error|RuntimeException t) {
127 throw t;
128 } catch(final Throwable t) {
129 throw new RuntimeException(t);
130 }
131 }
132
133 // Converts IR node to runtime value
134 private static Object convertNode(final ScriptObject global, final Node node) {
135 assert global instanceof GlobalObject;
136
137 if (node instanceof LiteralNode) {
138 // check for array literal
158
159 for (final Node elem : elements) {
160 values[index++] = convertNode(global, elem);
161 }
162
163 return ((GlobalObject)global).wrapAsObject(values);
164 }
165
166 return ((LiteralNode<?>)node).getValue();
167
168 } else if (node instanceof ObjectNode) {
169 final ObjectNode objNode = (ObjectNode) node;
170 final ScriptObject object = ((GlobalObject)global).newObject();
171 final boolean strict = global.isStrictContext();
172 final List<Node> elements = objNode.getElements();
173
174 for (final Node elem : elements) {
175 final PropertyNode pNode = (PropertyNode) elem;
176 final Node valueNode = pNode.getValue();
177
178 object.set(pNode.getKeyName(), convertNode(global, valueNode), strict);
179 }
180
181 return object;
182 } else if (node instanceof UnaryNode) {
183 // UnaryNode used only to represent negative number JSON value
184 final UnaryNode unaryNode = (UnaryNode)node;
185 return -((LiteralNode<?>)unaryNode.rhs()).getNumber();
186 } else {
187 return null;
188 }
189 }
190
191 // does the given IR node represent a numeric array?
192 private static boolean isNumericArray(final Node[] values) {
193 for (final Node node : values) {
194 if (node instanceof LiteralNode && ((LiteralNode<?>)node).getValue() instanceof Number) {
195 continue;
196 }
197 return false;
198 }
199 return true;
200 }
201 }
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.nashorn.internal.runtime;
27
28 import java.lang.invoke.MethodHandle;
29 import java.util.Iterator;
30 import java.util.List;
31 import jdk.nashorn.internal.ir.LiteralNode;
32 import jdk.nashorn.internal.ir.Node;
33 import jdk.nashorn.internal.ir.ObjectNode;
34 import jdk.nashorn.internal.ir.PropertyNode;
35 import jdk.nashorn.internal.ir.UnaryNode;
36 import jdk.nashorn.internal.parser.JSONParser;
37 import jdk.nashorn.internal.parser.TokenType;
38 import jdk.nashorn.internal.runtime.linker.Bootstrap;
39 import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndexNoThrow;
40 import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
41
42 /**
43 * Utilities used by "JSON" object implementation.
44 */
45 public final class JSONFunctions {
46 private JSONFunctions() {}
47 private static final MethodHandle REVIVER_INVOKER = Bootstrap.createDynamicInvoker("dyn:call", Object.class,
48 ScriptFunction.class, ScriptObject.class, String.class, Object.class);
49
50 /**
51 * Returns JSON-compatible quoted version of the given string.
52 *
53 * @param str String to be quoted
54 * @return JSON-compatible quoted string
55 */
56 public static String quote(final String str) {
57 return JSONParser.quote(str);
58 }
59
60 /**
79 try {
80 node = parser.parse();
81 } catch (final ParserException e) {
82 throw ECMAErrors.syntaxError(e, "invalid.json", e.getMessage());
83 }
84
85 final ScriptObject global = Context.getGlobalTrusted();
86 Object unfiltered = convertNode(global, node);
87 return applyReviver(global, unfiltered, reviver);
88 }
89
90 // -- Internals only below this point
91
92 // parse helpers
93
94 // apply 'reviver' function if available
95 private static Object applyReviver(final ScriptObject global, final Object unfiltered, final Object reviver) {
96 if (reviver instanceof ScriptFunction) {
97 assert global instanceof GlobalObject;
98 final ScriptObject root = ((GlobalObject)global).newObject();
99 root.addOwnProperty("", Property.WRITABLE_ENUMERABLE_CONFIGURABLE, unfiltered);
100 return walk(root, "", (ScriptFunction)reviver);
101 }
102 return unfiltered;
103 }
104
105 // This is the abstract "Walk" operation from the spec.
106 private static Object walk(final ScriptObject holder, final Object name, final ScriptFunction reviver) {
107 final Object val = holder.get(name);
108 if (val instanceof ScriptObject) {
109 final ScriptObject valueObj = (ScriptObject)val;
110 final boolean strict = valueObj.isStrictContext();
111 final Iterator<String> iter = valueObj.propertyIterator();
112
113 while (iter.hasNext()) {
114 final String key = iter.next();
115 final Object newElement = walk(valueObj, key, reviver);
116
117 if (newElement == ScriptRuntime.UNDEFINED) {
118 valueObj.delete(key, strict);
119 } else {
120 setPropertyValue(valueObj, key, newElement, strict);
121 }
122 }
123 }
124
125 try {
126 // Object.class, ScriptFunction.class, ScriptObject.class, String.class, Object.class);
127 return REVIVER_INVOKER.invokeExact(reviver, holder, JSType.toString(name), val);
128 } catch(Error|RuntimeException t) {
129 throw t;
130 } catch(final Throwable t) {
131 throw new RuntimeException(t);
132 }
133 }
134
135 // Converts IR node to runtime value
136 private static Object convertNode(final ScriptObject global, final Node node) {
137 assert global instanceof GlobalObject;
138
139 if (node instanceof LiteralNode) {
140 // check for array literal
160
161 for (final Node elem : elements) {
162 values[index++] = convertNode(global, elem);
163 }
164
165 return ((GlobalObject)global).wrapAsObject(values);
166 }
167
168 return ((LiteralNode<?>)node).getValue();
169
170 } else if (node instanceof ObjectNode) {
171 final ObjectNode objNode = (ObjectNode) node;
172 final ScriptObject object = ((GlobalObject)global).newObject();
173 final boolean strict = global.isStrictContext();
174 final List<Node> elements = objNode.getElements();
175
176 for (final Node elem : elements) {
177 final PropertyNode pNode = (PropertyNode) elem;
178 final Node valueNode = pNode.getValue();
179
180 final String name = pNode.getKeyName();
181 final Object value = convertNode(global, valueNode);
182 setPropertyValue(object, name, value, strict);
183 }
184
185 return object;
186 } else if (node instanceof UnaryNode) {
187 // UnaryNode used only to represent negative number JSON value
188 final UnaryNode unaryNode = (UnaryNode)node;
189 return -((LiteralNode<?>)unaryNode.rhs()).getNumber();
190 } else {
191 return null;
192 }
193 }
194
195 // add a new property if does not exist already, or else set old property
196 private static void setPropertyValue(final ScriptObject sobj, final String name, final Object value, final boolean strict) {
197 final int index = getArrayIndexNoThrow(name);
198 if (isValidArrayIndex(index)) {
199 // array index key
200 sobj.defineOwnProperty(index, value);
201 } else if (sobj.getMap().findProperty(name) != null) {
202 // pre-existing non-inherited property, call set
203 sobj.set(name, value, strict);
204 } else {
205 // add new property
206 sobj.addOwnProperty(name, Property.WRITABLE_ENUMERABLE_CONFIGURABLE, value);
207 }
208 }
209
210 // does the given IR node represent a numeric array?
211 private static boolean isNumericArray(final Node[] values) {
212 for (final Node node : values) {
213 if (node instanceof LiteralNode && ((LiteralNode<?>)node).getValue() instanceof Number) {
214 continue;
215 }
216 return false;
217 }
218 return true;
219 }
220 }
|