/*
 * 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.api.scripting;

import java.nio.ByteBuffer;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Callable;
import javax.script.Bindings;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.ConsString;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ECMAException;
import jdk.nashorn.internal.runtime.JSONListAdapter;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;

Mirror object that wraps a given Nashorn Script object.
Since:1.8u40
/** * Mirror object that wraps a given Nashorn Script object. * * @since 1.8u40 */
public final class ScriptObjectMirror extends AbstractJSObject implements Bindings { private static AccessControlContext getContextAccCtxt() { final Permissions perms = new Permissions(); perms.add(new RuntimePermission(Context.NASHORN_GET_CONTEXT)); return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) }); } private static final AccessControlContext GET_CONTEXT_ACC_CTXT = getContextAccCtxt(); private final ScriptObject sobj; private final Global global; private final boolean strict; private final boolean jsonCompatible; @Override public boolean equals(final Object other) { if (other instanceof ScriptObjectMirror) { return sobj.equals(((ScriptObjectMirror)other).sobj); } return false; } @Override public int hashCode() { return sobj.hashCode(); } @Override public String toString() { return inGlobal(new Callable<String>() { @Override public String call() { return ScriptRuntime.safeToString(sobj); } }); } // JSObject methods @Override public Object call(final Object thiz, final Object... args) { final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); try { if (globalChanged) { Context.setGlobal(global); } if (sobj instanceof ScriptFunction) { final Object[] modArgs = globalChanged? wrapArrayLikeMe(args, oldGlobal) : args; final Object self = globalChanged? wrapLikeMe(thiz, oldGlobal) : thiz; return wrapLikeMe(ScriptRuntime.apply((ScriptFunction)sobj, unwrap(self, global), unwrapArray(modArgs, global))); } throw new RuntimeException("not a function: " + toString()); } catch (final NashornException ne) { throw ne.initEcmaError(global); } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { throw new RuntimeException(t); } finally { if (globalChanged) { Context.setGlobal(oldGlobal); } } } @Override public Object newObject(final Object... args) { final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); try { if (globalChanged) { Context.setGlobal(global); } if (sobj instanceof ScriptFunction) { final Object[] modArgs = globalChanged? wrapArrayLikeMe(args, oldGlobal) : args; return wrapLikeMe(ScriptRuntime.construct((ScriptFunction)sobj, unwrapArray(modArgs, global))); } throw new RuntimeException("not a constructor: " + toString()); } catch (final NashornException ne) { throw ne.initEcmaError(global); } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { throw new RuntimeException(t); } finally { if (globalChanged) { Context.setGlobal(oldGlobal); } } } @Override public Object eval(final String s) { return inGlobal(new Callable<Object>() { @Override public Object call() { final Context context = AccessController.doPrivileged( new PrivilegedAction<Context>() { @Override public Context run() { return Context.getContext(); } }, GET_CONTEXT_ACC_CTXT); return wrapLikeMe(context.eval(global, s, sobj, null)); } }); }
Call member function
Params:
  • functionName – function name
  • args – arguments
Returns:return value of function
/** * Call member function * @param functionName function name * @param args arguments * @return return value of function */
public Object callMember(final String functionName, final Object... args) { Objects.requireNonNull(functionName); final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); try { if (globalChanged) { Context.setGlobal(global); } final Object val = sobj.get(functionName); if (val instanceof ScriptFunction) { final Object[] modArgs = globalChanged? wrapArrayLikeMe(args, oldGlobal) : args; return wrapLikeMe(ScriptRuntime.apply((ScriptFunction)val, sobj, unwrapArray(modArgs, global))); } else if (val instanceof JSObject && ((JSObject)val).isFunction()) { return ((JSObject)val).call(sobj, args); } throw new NoSuchMethodException("No such function " + functionName); } catch (final NashornException ne) { throw ne.initEcmaError(global); } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { throw new RuntimeException(t); } finally { if (globalChanged) { Context.setGlobal(oldGlobal); } } } @Override public Object getMember(final String name) { Objects.requireNonNull(name); return inGlobal(new Callable<Object>() { @Override public Object call() { return wrapLikeMe(sobj.get(name)); } }); } @Override public Object getSlot(final int index) { return inGlobal(new Callable<Object>() { @Override public Object call() { return wrapLikeMe(sobj.get(index)); } }); } @Override public boolean hasMember(final String name) { Objects.requireNonNull(name); return inGlobal(new Callable<Boolean>() { @Override public Boolean call() { return sobj.has(name); } }); } @Override public boolean hasSlot(final int slot) { return inGlobal(new Callable<Boolean>() { @Override public Boolean call() { return sobj.has(slot); } }); } @Override public void removeMember(final String name) { remove(Objects.requireNonNull(name)); } @Override public void setMember(final String name, final Object value) { put(Objects.requireNonNull(name), value); } @Override public void setSlot(final int index, final Object value) { inGlobal(new Callable<Void>() { @Override public Void call() { sobj.set(index, unwrap(value, global), getCallSiteFlags()); return null; } }); }
Nashorn extension: setIndexedPropertiesToExternalArrayData. set indexed properties be exposed from a given nio ByteBuffer.
Params:
  • buf – external buffer - should be a nio ByteBuffer
/** * Nashorn extension: setIndexedPropertiesToExternalArrayData. * set indexed properties be exposed from a given nio ByteBuffer. * * @param buf external buffer - should be a nio ByteBuffer */
public void setIndexedPropertiesToExternalArrayData(final ByteBuffer buf) { inGlobal(new Callable<Void>() { @Override public Void call() { sobj.setArray(ArrayData.allocate(buf)); return null; } }); } @Override public boolean isInstance(final Object instance) { if (! (instance instanceof ScriptObjectMirror)) { return false; } final ScriptObjectMirror mirror = (ScriptObjectMirror)instance; // if not belongs to my global scope, return false if (global != mirror.global) { return false; } return inGlobal(new Callable<Boolean>() { @Override public Boolean call() { return sobj.isInstance(mirror.sobj); } }); } @Override public String getClassName() { return sobj.getClassName(); } @Override public boolean isFunction() { return sobj instanceof ScriptFunction; } @Override public boolean isStrictFunction() { return isFunction() && ((ScriptFunction)sobj).isStrict(); } @Override public boolean isArray() { return sobj.isArray(); } // javax.script.Bindings methods @Override public void clear() { inGlobal(new Callable<Object>() { @Override public Object call() { sobj.clear(strict); return null; } }); } @Override public boolean containsKey(final Object key) { checkKey(key); return inGlobal(new Callable<Boolean>() { @Override public Boolean call() { return sobj.containsKey(key); } }); } @Override public boolean containsValue(final Object value) { return inGlobal(new Callable<Boolean>() { @Override public Boolean call() { return sobj.containsValue(unwrap(value, global)); } }); } @Override public Set<Map.Entry<String, Object>> entrySet() { return inGlobal(new Callable<Set<Map.Entry<String, Object>>>() { @Override public Set<Map.Entry<String, Object>> call() { final Iterator<String> iter = sobj.propertyIterator(); final Set<Map.Entry<String, Object>> entries = new LinkedHashSet<>(); while (iter.hasNext()) { final String key = iter.next(); final Object value = translateUndefined(wrapLikeMe(sobj.get(key))); entries.add(new AbstractMap.SimpleImmutableEntry<>(key, value)); } return Collections.unmodifiableSet(entries); } }); } @Override public Object get(final Object key) { checkKey(key); return inGlobal(new Callable<Object>() { @Override public Object call() { return translateUndefined(wrapLikeMe(sobj.get(key))); } }); } @Override public boolean isEmpty() { return inGlobal(new Callable<Boolean>() { @Override public Boolean call() { return sobj.isEmpty(); } }); } @Override public Set<String> keySet() { return inGlobal(new Callable<Set<String>>() { @Override public Set<String> call() { final Iterator<String> iter = sobj.propertyIterator(); final Set<String> keySet = new LinkedHashSet<>(); while (iter.hasNext()) { keySet.add(iter.next()); } return Collections.unmodifiableSet(keySet); } }); } @Override public Object put(final String key, final Object value) { checkKey(key); final ScriptObject oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); return inGlobal(new Callable<Object>() { @Override public Object call() { final Object modValue = globalChanged? wrapLikeMe(value, oldGlobal) : value; return translateUndefined(wrapLikeMe(sobj.put(key, unwrap(modValue, global), strict))); } }); } @Override public void putAll(final Map<? extends String, ? extends Object> map) { Objects.requireNonNull(map); final ScriptObject oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); inGlobal(new Callable<Object>() { @Override public Object call() { for (final Map.Entry<? extends String, ? extends Object> entry : map.entrySet()) { final Object value = entry.getValue(); final Object modValue = globalChanged? wrapLikeMe(value, oldGlobal) : value; final String key = entry.getKey(); checkKey(key); sobj.set(key, unwrap(modValue, global), getCallSiteFlags()); } return null; } }); } @Override public Object remove(final Object key) { checkKey(key); return inGlobal(new Callable<Object>() { @Override public Object call() { return translateUndefined(wrapLikeMe(sobj.remove(key, strict))); } }); }
Delete a property from this object.
Params:
  • key – the property to be deleted
Returns:if the delete was successful or not
/** * Delete a property from this object. * * @param key the property to be deleted * * @return if the delete was successful or not */
public boolean delete(final Object key) { return inGlobal(new Callable<Boolean>() { @Override public Boolean call() { return sobj.delete(unwrap(key, global), strict); } }); } @Override public int size() { return inGlobal(new Callable<Integer>() { @Override public Integer call() { return sobj.size(); } }); } @Override public Collection<Object> values() { return inGlobal(new Callable<Collection<Object>>() { @Override public Collection<Object> call() { final List<Object> values = new ArrayList<>(size()); final Iterator<Object> iter = sobj.valueIterator(); while (iter.hasNext()) { values.add(translateUndefined(wrapLikeMe(iter.next()))); } return Collections.unmodifiableList(values); } }); } // Support for ECMAScript Object API on mirrors
Return the __proto__ of this object.
Returns:__proto__ object.
/** * Return the __proto__ of this object. * @return __proto__ object. */
public Object getProto() { return inGlobal(new Callable<Object>() { @Override public Object call() { return wrapLikeMe(sobj.getProto()); } }); }
Set the __proto__ of this object.
Params:
  • proto – new proto for this object
/** * Set the __proto__ of this object. * @param proto new proto for this object */
public void setProto(final Object proto) { inGlobal(new Callable<Void>() { @Override public Void call() { sobj.setPrototypeOf(unwrap(proto, global)); return null; } }); }
ECMA 8.12.1 [[GetOwnProperty]] (P)
Params:
  • key – property key
Returns:Returns the Property Descriptor of the named own property of this object, or undefined if absent.
/** * ECMA 8.12.1 [[GetOwnProperty]] (P) * * @param key property key * * @return Returns the Property Descriptor of the named own property of this * object, or undefined if absent. */
public Object getOwnPropertyDescriptor(final String key) { return inGlobal(new Callable<Object>() { @Override public Object call() { return wrapLikeMe(sobj.getOwnPropertyDescriptor(key)); } }); }
return an array of own property keys associated with the object.
Params:
  • all – True if to include non-enumerable keys.
Returns:Array of keys.
/** * return an array of own property keys associated with the object. * * @param all True if to include non-enumerable keys. * @return Array of keys. */
public String[] getOwnKeys(final boolean all) { return inGlobal(new Callable<String[]>() { @Override public String[] call() { return sobj.getOwnKeys(all); } }); }
Flag this script object as non extensible
Returns:the object after being made non extensible
/** * Flag this script object as non extensible * * @return the object after being made non extensible */
public ScriptObjectMirror preventExtensions() { return inGlobal(new Callable<ScriptObjectMirror>() { @Override public ScriptObjectMirror call() { sobj.preventExtensions(); return ScriptObjectMirror.this; } }); }
Check if this script object is extensible
Returns:true if extensible
/** * Check if this script object is extensible * @return true if extensible */
public boolean isExtensible() { return inGlobal(new Callable<Boolean>() { @Override public Boolean call() { return sobj.isExtensible(); } }); }
ECMAScript 15.2.3.8 - seal implementation
Returns:the sealed script object
/** * ECMAScript 15.2.3.8 - seal implementation * @return the sealed script object */
public ScriptObjectMirror seal() { return inGlobal(new Callable<ScriptObjectMirror>() { @Override public ScriptObjectMirror call() { sobj.seal(); return ScriptObjectMirror.this; } }); }
Check whether this script object is sealed
Returns:true if sealed
/** * Check whether this script object is sealed * @return true if sealed */
public boolean isSealed() { return inGlobal(new Callable<Boolean>() { @Override public Boolean call() { return sobj.isSealed(); } }); }
ECMA 15.2.39 - freeze implementation. Freeze this script object
Returns:the frozen script object
/** * ECMA 15.2.39 - freeze implementation. Freeze this script object * @return the frozen script object */
public ScriptObjectMirror freeze() { return inGlobal(new Callable<ScriptObjectMirror>() { @Override public ScriptObjectMirror call() { sobj.freeze(); return ScriptObjectMirror.this; } }); }
Check whether this script object is frozen
Returns:true if frozen
/** * Check whether this script object is frozen * @return true if frozen */
public boolean isFrozen() { return inGlobal(new Callable<Boolean>() { @Override public Boolean call() { return sobj.isFrozen(); } }); }
Utility to check if given object is ECMAScript undefined value
Params:
  • obj – object to check
Returns:true if 'obj' is ECMAScript undefined value
/** * Utility to check if given object is ECMAScript undefined value * * @param obj object to check * @return true if 'obj' is ECMAScript undefined value */
public static boolean isUndefined(final Object obj) { return obj == ScriptRuntime.UNDEFINED; }
Utility to convert this script object to the given type.
Params:
  • type – destination type to convert to
Type parameters:
  • <T> – destination type to convert to
Returns:converted object
/** * Utility to convert this script object to the given type. * * @param <T> destination type to convert to * @param type destination type to convert to * @return converted object */
public <T> T to(final Class<T> type) { return inGlobal(new Callable<T>() { @Override public T call() { return type.cast(ScriptUtils.convert(sobj, type)); } }); }
Make a script object mirror on given object if needed.
Params:
  • obj – object to be wrapped/converted
  • homeGlobal – global to which this object belongs.
Returns:wrapped/converted object
/** * Make a script object mirror on given object if needed. * * @param obj object to be wrapped/converted * @param homeGlobal global to which this object belongs. * @return wrapped/converted object */
public static Object wrap(final Object obj, final Object homeGlobal) { return wrap(obj, homeGlobal, false); }
Make a script object mirror on given object if needed. The created wrapper will implement the Java List interface if obj is a JavaScript Array object; this is compatible with Java JSON libraries expectations. Arrays retrieved through its properties (transitively) will also implement the list interface.
Params:
  • obj – object to be wrapped/converted
  • homeGlobal – global to which this object belongs.
Returns:wrapped/converted object
/** * Make a script object mirror on given object if needed. The created wrapper will implement * the Java {@code List} interface if {@code obj} is a JavaScript {@code Array} object; * this is compatible with Java JSON libraries expectations. Arrays retrieved through its * properties (transitively) will also implement the list interface. * * @param obj object to be wrapped/converted * @param homeGlobal global to which this object belongs. * @return wrapped/converted object */
public static Object wrapAsJSONCompatible(final Object obj, final Object homeGlobal) { return wrap(obj, homeGlobal, true); }
Make a script object mirror on given object if needed.
Params:
  • obj – object to be wrapped/converted
  • homeGlobal – global to which this object belongs.
  • jsonCompatible – if true, the created wrapper will implement the Java List interface if obj is a JavaScript Array object. Arrays retrieved through its properties (transitively) will also implement the list interface.
Returns:wrapped/converted object
/** * Make a script object mirror on given object if needed. * * @param obj object to be wrapped/converted * @param homeGlobal global to which this object belongs. * @param jsonCompatible if true, the created wrapper will implement the Java {@code List} interface if * {@code obj} is a JavaScript {@code Array} object. Arrays retrieved through its properties (transitively) * will also implement the list interface. * @return wrapped/converted object */
private static Object wrap(final Object obj, final Object homeGlobal, final boolean jsonCompatible) { if(obj instanceof ScriptObject) { if (!(homeGlobal instanceof Global)) { return obj; } final ScriptObject sobj = (ScriptObject)obj; final Global global = (Global)homeGlobal; final ScriptObjectMirror mirror = new ScriptObjectMirror(sobj, global, jsonCompatible); if (jsonCompatible && sobj.isArray()) { return new JSONListAdapter(mirror, global); } return mirror; } else if(obj instanceof ConsString) { return obj.toString(); } else if (jsonCompatible && obj instanceof ScriptObjectMirror) { // Since choosing JSON compatible representation is an explicit decision on user's part, if we're asked to // wrap a mirror that was not JSON compatible, explicitly create its compatible counterpart following the // principle of least surprise. return ((ScriptObjectMirror)obj).asJSONCompatible(); } return obj; }
Wraps the passed object with the same jsonCompatible flag as this mirror.
Params:
  • obj – the object
  • homeGlobal – the object's home global.
Returns:a wrapper for the object.
/** * Wraps the passed object with the same jsonCompatible flag as this mirror. * @param obj the object * @param homeGlobal the object's home global. * @return a wrapper for the object. */
private Object wrapLikeMe(final Object obj, final Object homeGlobal) { return wrap(obj, homeGlobal, jsonCompatible); }
Wraps the passed object with the same home global and jsonCompatible flag as this mirror.
Params:
  • obj – the object
Returns:a wrapper for the object.
/** * Wraps the passed object with the same home global and jsonCompatible flag as this mirror. * @param obj the object * @return a wrapper for the object. */
private Object wrapLikeMe(final Object obj) { return wrapLikeMe(obj, global); }
Unwrap a script object mirror if needed.
Params:
  • obj – object to be unwrapped
  • homeGlobal – global to which this object belongs
Returns:unwrapped object
/** * Unwrap a script object mirror if needed. * * @param obj object to be unwrapped * @param homeGlobal global to which this object belongs * @return unwrapped object */
public static Object unwrap(final Object obj, final Object homeGlobal) { if (obj instanceof ScriptObjectMirror) { final ScriptObjectMirror mirror = (ScriptObjectMirror)obj; return (mirror.global == homeGlobal)? mirror.sobj : obj; } else if (obj instanceof JSONListAdapter) { return ((JSONListAdapter)obj).unwrap(homeGlobal); } return obj; }
Wrap an array of object to script object mirrors if needed.
Params:
  • args – array to be unwrapped
  • homeGlobal – global to which this object belongs
Returns:wrapped array
/** * Wrap an array of object to script object mirrors if needed. * * @param args array to be unwrapped * @param homeGlobal global to which this object belongs * @return wrapped array */
public static Object[] wrapArray(final Object[] args, final Object homeGlobal) { return wrapArray(args, homeGlobal, false); } private static Object[] wrapArray(final Object[] args, final Object homeGlobal, final boolean jsonCompatible) { if (args == null || args.length == 0) { return args; } final Object[] newArgs = new Object[args.length]; int index = 0; for (final Object obj : args) { newArgs[index] = wrap(obj, homeGlobal, jsonCompatible); index++; } return newArgs; } private Object[] wrapArrayLikeMe(final Object[] args, final Object homeGlobal) { return wrapArray(args, homeGlobal, jsonCompatible); }
Unwrap an array of script object mirrors if needed.
Params:
  • args – array to be unwrapped
  • homeGlobal – global to which this object belongs
Returns:unwrapped array
/** * Unwrap an array of script object mirrors if needed. * * @param args array to be unwrapped * @param homeGlobal global to which this object belongs * @return unwrapped array */
public static Object[] unwrapArray(final Object[] args, final Object homeGlobal) { if (args == null || args.length == 0) { return args; } final Object[] newArgs = new Object[args.length]; int index = 0; for (final Object obj : args) { newArgs[index] = unwrap(obj, homeGlobal); index++; } return newArgs; }
Are the given objects mirrors to same underlying object?
Params:
  • obj1 – first object
  • obj2 – second object
Returns:true if obj1 and obj2 are identical script objects or mirrors of it.
/** * Are the given objects mirrors to same underlying object? * * @param obj1 first object * @param obj2 second object * @return true if obj1 and obj2 are identical script objects or mirrors of it. */
public static boolean identical(final Object obj1, final Object obj2) { final Object o1 = (obj1 instanceof ScriptObjectMirror)? ((ScriptObjectMirror)obj1).sobj : obj1; final Object o2 = (obj2 instanceof ScriptObjectMirror)? ((ScriptObjectMirror)obj2).sobj : obj2; return o1 == o2; } // package-privates below this. ScriptObjectMirror(final ScriptObject sobj, final Global global) { this(sobj, global, false); } private ScriptObjectMirror(final ScriptObject sobj, final Global global, final boolean jsonCompatible) { assert sobj != null : "ScriptObjectMirror on null!"; assert global != null : "home Global is null"; this.sobj = sobj; this.global = global; this.strict = global.isStrictContext(); this.jsonCompatible = jsonCompatible; } // accessors for script engine ScriptObject getScriptObject() { return sobj; } Global getHomeGlobal() { return global; } static Object translateUndefined(final Object obj) { return (obj == ScriptRuntime.UNDEFINED)? null : obj; } private int getCallSiteFlags() { return strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0; } // internals only below this. private <V> V inGlobal(final Callable<V> callable) { final Global oldGlobal = Context.getGlobal(); final boolean globalChanged = (oldGlobal != global); if (globalChanged) { Context.setGlobal(global); } try { return callable.call(); } catch (final NashornException ne) { throw ne.initEcmaError(global); } catch (final RuntimeException e) { throw e; } catch (final Exception e) { throw new AssertionError("Cannot happen", e); } finally { if (globalChanged) { Context.setGlobal(oldGlobal); } } }
Ensures the key is not null, empty string, or a non-String object. The contract of the Bindings interface requires that these are not accepted as keys.
Params:
  • key – the key to check
Throws:
/** * Ensures the key is not null, empty string, or a non-String object. The contract of the {@link Bindings} * interface requires that these are not accepted as keys. * @param key the key to check * @throws NullPointerException if key is null * @throws ClassCastException if key is not a String * @throws IllegalArgumentException if key is empty string */
private static void checkKey(final Object key) { Objects.requireNonNull(key, "key can not be null"); if (!(key instanceof String)) { throw new ClassCastException("key should be a String. It is " + key.getClass().getName() + " instead."); } else if (((String)key).length() == 0) { throw new IllegalArgumentException("key can not be empty"); } } @Override @Deprecated public double toNumber() { return inGlobal(new Callable<Double>() { @Override public Double call() { return JSType.toNumber(sobj); } }); } @Override public Object getDefaultValue(final Class<?> hint) { return inGlobal(new Callable<Object>() { @Override public Object call() { try { return sobj.getDefaultValue(hint); } catch (final ECMAException e) { // We're catching ECMAException (likely TypeError), and translating it to // UnsupportedOperationException. This in turn will be translated into TypeError of the // caller's Global by JSType#toPrimitive(JSObject,Class) therefore ensuring that it's // recognized as "instanceof TypeError" in the caller. throw new UnsupportedOperationException(e.getMessage(), e); } } }); } private ScriptObjectMirror asJSONCompatible() { if (this.jsonCompatible) { return this; } return new ScriptObjectMirror(sobj, global, true); } }