--- old/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJSON.java 2020-04-15 18:48:44.000000000 +0530 +++ /dev/null 2020-04-15 18:48:44.000000000 +0530 @@ -1,486 +0,0 @@ -/* - * 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package jdk.nashorn.internal.objects; - -import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; -import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; - -import java.lang.invoke.MethodHandle; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.Callable; -import jdk.nashorn.api.scripting.JSObject; -import jdk.nashorn.api.scripting.ScriptObjectMirror; -import jdk.nashorn.internal.objects.annotations.Attribute; -import jdk.nashorn.internal.objects.annotations.Function; -import jdk.nashorn.internal.objects.annotations.ScriptClass; -import jdk.nashorn.internal.objects.annotations.Where; -import jdk.nashorn.internal.runtime.ConsString; -import jdk.nashorn.internal.runtime.JSONFunctions; -import jdk.nashorn.internal.runtime.JSType; -import jdk.nashorn.internal.runtime.PropertyMap; -import jdk.nashorn.internal.runtime.ScriptObject; -import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator; -import jdk.nashorn.internal.runtime.linker.Bootstrap; -import jdk.nashorn.internal.runtime.linker.InvokeByName; - -/** - * ECMAScript 262 Edition 5, Section 15.12 The NativeJSON Object - * - */ -@ScriptClass("JSON") -public final class NativeJSON extends ScriptObject { - private static final Object TO_JSON = new Object(); - - private static InvokeByName getTO_JSON() { - return Global.instance().getInvokeByName(TO_JSON, - new Callable() { - @Override - public InvokeByName call() { - return new InvokeByName("toJSON", ScriptObject.class, Object.class, Object.class); - } - }); - } - - private static final Object JSOBJECT_INVOKER = new Object(); - - private static MethodHandle getJSOBJECT_INVOKER() { - return Global.instance().getDynamicInvoker(JSOBJECT_INVOKER, - new Callable() { - @Override - public MethodHandle call() { - return Bootstrap.createDynamicCallInvoker(Object.class, Object.class, Object.class); - } - }); - } - - private static final Object REPLACER_INVOKER = new Object(); - - private static MethodHandle getREPLACER_INVOKER() { - return Global.instance().getDynamicInvoker(REPLACER_INVOKER, - new Callable() { - @Override - public MethodHandle call() { - return Bootstrap.createDynamicCallInvoker(Object.class, - Object.class, Object.class, Object.class, Object.class); - } - }); - } - - // initialized by nasgen - @SuppressWarnings("unused") - private static PropertyMap $nasgenmap$; - - private NativeJSON() { - // don't create me!! - throw new UnsupportedOperationException(); - } - - /** - * ECMA 15.12.2 parse ( text [ , reviver ] ) - * - * @param self self reference - * @param text a JSON formatted string - * @param reviver optional value: function that takes two parameters (key, value) - * - * @return an ECMA script value - */ - @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object parse(final Object self, final Object text, final Object reviver) { - return JSONFunctions.parse(text, reviver); - } - - /** - * ECMA 15.12.3 stringify ( value [ , replacer [ , space ] ] ) - * - * @param self self reference - * @param value ECMA script value (usually object or array) - * @param replacer either a function or an array of strings and numbers - * @param space optional parameter - allows result to have whitespace injection - * - * @return a string in JSON format - */ - @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) - public static Object stringify(final Object self, final Object value, final Object replacer, final Object space) { - // The stringify method takes a value and an optional replacer, and an optional - // space parameter, and returns a JSON text. The replacer can be a function - // that can replace values, or an array of strings that will select the keys. - - // A default replacer method can be provided. Use of the space parameter can - // produce text that is more easily readable. - - final StringifyState state = new StringifyState(); - - // If there is a replacer, it must be a function or an array. - if (Bootstrap.isCallable(replacer)) { - state.replacerFunction = replacer; - } else if (isArray(replacer) || - isJSObjectArray(replacer) || - replacer instanceof Iterable || - (replacer != null && replacer.getClass().isArray())) { - - state.propertyList = new ArrayList<>(); - - final Iterator iter = ArrayLikeIterator.arrayLikeIterator(replacer); - - while (iter.hasNext()) { - String item = null; - final Object v = iter.next(); - - if (v instanceof String) { - item = (String) v; - } else if (v instanceof ConsString) { - item = v.toString(); - } else if (v instanceof Number || - v instanceof NativeNumber || - v instanceof NativeString) { - item = JSType.toString(v); - } - - if (item != null) { - state.propertyList.add(item); - } - } - } - - // If the space parameter is a number, make an indent - // string containing that many spaces. - - String gap; - - // modifiable 'space' - parameter is final - Object modSpace = space; - if (modSpace instanceof NativeNumber) { - modSpace = JSType.toNumber(JSType.toPrimitive(modSpace, Number.class)); - } else if (modSpace instanceof NativeString) { - modSpace = JSType.toString(JSType.toPrimitive(modSpace, String.class)); - } - - if (modSpace instanceof Number) { - final int indent = Math.min(10, JSType.toInteger(modSpace)); - if (indent < 1) { - gap = ""; - } else { - final StringBuilder sb = new StringBuilder(); - for (int i = 0; i < indent; i++) { - sb.append(' '); - } - gap = sb.toString(); - } - } else if (JSType.isString(modSpace)) { - final String str = modSpace.toString(); - gap = str.substring(0, Math.min(10, str.length())); - } else { - gap = ""; - } - - state.gap = gap; - - final ScriptObject wrapper = Global.newEmptyInstance(); - wrapper.set("", value, 0); - - return str("", wrapper, state); - } - - // -- Internals only below this point - - // stringify helpers. - - private static class StringifyState { - final Map stack = new IdentityHashMap<>(); - - StringBuilder indent = new StringBuilder(); - String gap = ""; - List propertyList = null; - Object replacerFunction = null; - } - - // Spec: The abstract operation Str(key, holder). - private static Object str(final Object key, final Object holder, final StringifyState state) { - assert holder instanceof ScriptObject || holder instanceof JSObject; - - Object value = getProperty(holder, key); - try { - if (value instanceof ScriptObject) { - final InvokeByName toJSONInvoker = getTO_JSON(); - final ScriptObject svalue = (ScriptObject)value; - final Object toJSON = toJSONInvoker.getGetter().invokeExact(svalue); - if (Bootstrap.isCallable(toJSON)) { - value = toJSONInvoker.getInvoker().invokeExact(toJSON, svalue, key); - } - } else if (value instanceof JSObject) { - final JSObject jsObj = (JSObject)value; - final Object toJSON = jsObj.getMember("toJSON"); - if (Bootstrap.isCallable(toJSON)) { - value = getJSOBJECT_INVOKER().invokeExact(toJSON, value); - } - } - - if (state.replacerFunction != null) { - value = getREPLACER_INVOKER().invokeExact(state.replacerFunction, holder, key, value); - } - } catch(Error|RuntimeException t) { - throw t; - } catch(final Throwable t) { - throw new RuntimeException(t); - } - final boolean isObj = (value instanceof ScriptObject); - if (isObj) { - if (value instanceof NativeNumber) { - value = JSType.toNumber(value); - } else if (value instanceof NativeString) { - value = JSType.toString(value); - } else if (value instanceof NativeBoolean) { - value = ((NativeBoolean)value).booleanValue(); - } - } - - if (value == null) { - return "null"; - } else if (Boolean.TRUE.equals(value)) { - return "true"; - } else if (Boolean.FALSE.equals(value)) { - return "false"; - } - - if (value instanceof String) { - return JSONFunctions.quote((String)value); - } else if (value instanceof ConsString) { - return JSONFunctions.quote(value.toString()); - } - - if (value instanceof Number) { - return JSType.isFinite(((Number)value).doubleValue()) ? JSType.toString(value) : "null"; - } - - final JSType type = JSType.of(value); - if (type == JSType.OBJECT) { - if (isArray(value) || isJSObjectArray(value)) { - return JA(value, state); - } else if (value instanceof ScriptObject || value instanceof JSObject) { - return JO(value, state); - } - } - - return UNDEFINED; - } - - // Spec: The abstract operation JO(value) serializes an object. - private static String JO(final Object value, final StringifyState state) { - assert value instanceof ScriptObject || value instanceof JSObject; - - if (state.stack.containsKey(value)) { - throw typeError("JSON.stringify.cyclic"); - } - - state.stack.put(value, value); - final StringBuilder stepback = new StringBuilder(state.indent.toString()); - state.indent.append(state.gap); - - final StringBuilder finalStr = new StringBuilder(); - final List partial = new ArrayList<>(); - final List k = state.propertyList == null ? - Arrays.asList(getOwnKeys(value)) : state.propertyList; - - for (final Object p : k) { - final Object strP = str(p, value, state); - - if (strP != UNDEFINED) { - final StringBuilder member = new StringBuilder(); - - member.append(JSONFunctions.quote(p.toString())).append(':'); - if (!state.gap.isEmpty()) { - member.append(' '); - } - - member.append(strP); - partial.add(member); - } - } - - if (partial.isEmpty()) { - finalStr.append("{}"); - } else { - if (state.gap.isEmpty()) { - final int size = partial.size(); - int index = 0; - - finalStr.append('{'); - - for (final Object str : partial) { - finalStr.append(str); - if (index < size - 1) { - finalStr.append(','); - } - index++; - } - - finalStr.append('}'); - } else { - final int size = partial.size(); - int index = 0; - - finalStr.append("{\n"); - finalStr.append(state.indent); - - for (final Object str : partial) { - finalStr.append(str); - if (index < size - 1) { - finalStr.append(",\n"); - finalStr.append(state.indent); - } - index++; - } - - finalStr.append('\n'); - finalStr.append(stepback); - finalStr.append('}'); - } - } - - state.stack.remove(value); - state.indent = stepback; - - return finalStr.toString(); - } - - // Spec: The abstract operation JA(value) serializes an array. - private static Object JA(final Object value, final StringifyState state) { - assert value instanceof ScriptObject || value instanceof JSObject; - - if (state.stack.containsKey(value)) { - throw typeError("JSON.stringify.cyclic"); - } - - state.stack.put(value, value); - final StringBuilder stepback = new StringBuilder(state.indent.toString()); - state.indent.append(state.gap); - final List partial = new ArrayList<>(); - - final int length = JSType.toInteger(getLength(value)); - int index = 0; - - while (index < length) { - Object strP = str(index, value, state); - if (strP == UNDEFINED) { - strP = "null"; - } - partial.add(strP); - index++; - } - - final StringBuilder finalStr = new StringBuilder(); - if (partial.isEmpty()) { - finalStr.append("[]"); - } else { - if (state.gap.isEmpty()) { - final int size = partial.size(); - index = 0; - finalStr.append('['); - for (final Object str : partial) { - finalStr.append(str); - if (index < size - 1) { - finalStr.append(','); - } - index++; - } - - finalStr.append(']'); - } else { - final int size = partial.size(); - index = 0; - finalStr.append("[\n"); - finalStr.append(state.indent); - for (final Object str : partial) { - finalStr.append(str); - if (index < size - 1) { - finalStr.append(",\n"); - finalStr.append(state.indent); - } - index++; - } - - finalStr.append('\n'); - finalStr.append(stepback); - finalStr.append(']'); - } - } - - state.stack.remove(value); - state.indent = stepback; - - return finalStr.toString(); - } - - private static String[] getOwnKeys(final Object obj) { - if (obj instanceof ScriptObject) { - return ((ScriptObject)obj).getOwnKeys(false); - } else if (obj instanceof ScriptObjectMirror) { - return ((ScriptObjectMirror)obj).getOwnKeys(false); - } else if (obj instanceof JSObject) { - // No notion of "own keys" or "proto" for general JSObject! We just - // return all keys of the object. This will be useful for POJOs - // implementing JSObject interface. - return ((JSObject)obj).keySet().toArray(new String[0]); - } else { - throw new AssertionError("should not reach here"); - } - } - - private static Object getLength(final Object obj) { - if (obj instanceof ScriptObject) { - return ((ScriptObject)obj).getLength(); - } else if (obj instanceof JSObject) { - return ((JSObject)obj).getMember("length"); - } else { - throw new AssertionError("should not reach here"); - } - } - - private static boolean isJSObjectArray(final Object obj) { - return (obj instanceof JSObject) && ((JSObject)obj).isArray(); - } - - private static Object getProperty(final Object holder, final Object key) { - if (holder instanceof ScriptObject) { - return ((ScriptObject)holder).get(key); - } else if (holder instanceof JSObject) { - final JSObject jsObj = (JSObject)holder; - if (key instanceof Integer) { - return jsObj.getSlot((Integer)key); - } else { - return jsObj.getMember(Objects.toString(key)); - } - } else { - return new AssertionError("should not reach here"); - } - } -}