--- old/src/share/classes/com/sun/script/javascript/ExternalScriptable.java 2013-05-10 13:34:26.404901191 +0530 +++ /dev/null 2013-05-10 12:59:49.904693460 +0530 @@ -1,467 +0,0 @@ -/* - * Copyright (c) 2005, 2006, 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 com.sun.script.javascript; -import sun.org.mozilla.javascript.internal.*; -import javax.script.*; -import java.util.*; - -/** - * ExternalScriptable is an implementation of Scriptable - * backed by a JSR 223 ScriptContext instance. - * - * @author Mike Grogan - * @author A. Sundararajan - * @since 1.6 - */ - -final class ExternalScriptable implements Scriptable { - /* Underlying ScriptContext that we use to store - * named variables of this scope. - */ - private ScriptContext context; - - /* JavaScript allows variables to be named as numbers (indexed - * properties). This way arrays, objects (scopes) are treated uniformly. - * Note that JSR 223 API supports only String named variables and - * so we can't store these in Bindings. Also, JavaScript allows name - * of the property name to be even empty String! Again, JSR 223 API - * does not support empty name. So, we use the following fallback map - * to store such variables of this scope. This map is not exposed to - * JSR 223 API. We can just script objects "as is" and need not convert. - */ - private Map indexedProps; - - // my prototype - private Scriptable prototype; - // my parent scope, if any - private Scriptable parent; - - ExternalScriptable(ScriptContext context) { - this(context, new HashMap()); - } - - ExternalScriptable(ScriptContext context, Map indexedProps) { - if (context == null) { - throw new NullPointerException("context is null"); - } - this.context = context; - this.indexedProps = indexedProps; - } - - ScriptContext getContext() { - return context; - } - - private boolean isEmpty(String name) { - return name.equals(""); - } - - /** - * Return the name of the class. - */ - public String getClassName() { - return "Global"; - } - - /** - * Returns the value of the named property or NOT_FOUND. - * - * If the property was created using defineProperty, the - * appropriate getter method is called. - * - * @param name the name of the property - * @param start the object in which the lookup began - * @return the value of the property (may be null), or NOT_FOUND - */ - public synchronized Object get(String name, Scriptable start) { - if (isEmpty(name)) { - if (indexedProps.containsKey(name)) { - return indexedProps.get(name); - } else { - return NOT_FOUND; - } - } else { - synchronized (context) { - int scope = context.getAttributesScope(name); - if (scope != -1) { - Object value = context.getAttribute(name, scope); - return Context.javaToJS(value, this); - } else { - return NOT_FOUND; - } - } - } - } - - /** - * Returns the value of the indexed property or NOT_FOUND. - * - * @param index the numeric index for the property - * @param start the object in which the lookup began - * @return the value of the property (may be null), or NOT_FOUND - */ - public synchronized Object get(int index, Scriptable start) { - Integer key = new Integer(index); - if (indexedProps.containsKey(index)) { - return indexedProps.get(key); - } else { - return NOT_FOUND; - } - } - - /** - * Returns true if the named property is defined. - * - * @param name the name of the property - * @param start the object in which the lookup began - * @return true if and only if the property was found in the object - */ - public synchronized boolean has(String name, Scriptable start) { - if (isEmpty(name)) { - return indexedProps.containsKey(name); - } else { - synchronized (context) { - return context.getAttributesScope(name) != -1; - } - } - } - - /** - * Returns true if the property index is defined. - * - * @param index the numeric index for the property - * @param start the object in which the lookup began - * @return true if and only if the property was found in the object - */ - public synchronized boolean has(int index, Scriptable start) { - Integer key = new Integer(index); - return indexedProps.containsKey(key); - } - - /** - * Sets the value of the named property, creating it if need be. - * - * @param name the name of the property - * @param start the object whose property is being set - * @param value value to set the property to - */ - public void put(String name, Scriptable start, Object value) { - if (start == this) { - synchronized (this) { - if (isEmpty(name)) { - indexedProps.put(name, value); - } else { - synchronized (context) { - int scope = context.getAttributesScope(name); - if (scope == -1) { - scope = ScriptContext.ENGINE_SCOPE; - } - context.setAttribute(name, jsToJava(value), scope); - } - } - } - } else { - start.put(name, start, value); - } - } - - /** - * Sets the value of the indexed property, creating it if need be. - * - * @param index the numeric index for the property - * @param start the object whose property is being set - * @param value value to set the property to - */ - public void put(int index, Scriptable start, Object value) { - if (start == this) { - synchronized (this) { - indexedProps.put(new Integer(index), value); - } - } else { - start.put(index, start, value); - } - } - - /** - * Removes a named property from the object. - * - * If the property is not found, no action is taken. - * - * @param name the name of the property - */ - public synchronized void delete(String name) { - if (isEmpty(name)) { - indexedProps.remove(name); - } else { - synchronized (context) { - int scope = context.getAttributesScope(name); - if (scope != -1) { - context.removeAttribute(name, scope); - } - } - } - } - - /** - * Removes the indexed property from the object. - * - * If the property is not found, no action is taken. - * - * @param index the numeric index for the property - */ - public void delete(int index) { - indexedProps.remove(new Integer(index)); - } - - /** - * Get the prototype of the object. - * @return the prototype - */ - public Scriptable getPrototype() { - return prototype; - } - - /** - * Set the prototype of the object. - * @param prototype the prototype to set - */ - public void setPrototype(Scriptable prototype) { - this.prototype = prototype; - } - - /** - * Get the parent scope of the object. - * @return the parent scope - */ - public Scriptable getParentScope() { - return parent; - } - - /** - * Set the parent scope of the object. - * @param parent the parent scope to set - */ - public void setParentScope(Scriptable parent) { - this.parent = parent; - } - - /** - * Get an array of property ids. - * - * Not all property ids need be returned. Those properties - * whose ids are not returned are considered non-enumerable. - * - * @return an array of Objects. Each entry in the array is either - * a java.lang.String or a java.lang.Number - */ - public synchronized Object[] getIds() { - String[] keys = getAllKeys(); - int size = keys.length + indexedProps.size(); - Object[] res = new Object[size]; - System.arraycopy(keys, 0, res, 0, keys.length); - int i = keys.length; - // now add all indexed properties - for (Object index : indexedProps.keySet()) { - res[i++] = index; - } - return res; - } - - /** - * Get the default value of the object with a given hint. - * The hints are String.class for type String, Number.class for type - * Number, Scriptable.class for type Object, and Boolean.class for - * type Boolean.

- * - * A hint of null means "no hint". - * - * See ECMA 8.6.2.6. - * - * @param hint the type hint - * @return the default value - */ - public Object getDefaultValue(Class typeHint) { - for (int i=0; i < 2; i++) { - boolean tryToString; - if (typeHint == ScriptRuntime.StringClass) { - tryToString = (i == 0); - } else { - tryToString = (i == 1); - } - - String methodName; - Object[] args; - if (tryToString) { - methodName = "toString"; - args = ScriptRuntime.emptyArgs; - } else { - methodName = "valueOf"; - args = new Object[1]; - String hint; - if (typeHint == null) { - hint = "undefined"; - } else if (typeHint == ScriptRuntime.StringClass) { - hint = "string"; - } else if (typeHint == ScriptRuntime.ScriptableClass) { - hint = "object"; - } else if (typeHint == ScriptRuntime.FunctionClass) { - hint = "function"; - } else if (typeHint == ScriptRuntime.BooleanClass - || typeHint == Boolean.TYPE) - { - hint = "boolean"; - } else if (typeHint == ScriptRuntime.NumberClass || - typeHint == ScriptRuntime.ByteClass || - typeHint == Byte.TYPE || - typeHint == ScriptRuntime.ShortClass || - typeHint == Short.TYPE || - typeHint == ScriptRuntime.IntegerClass || - typeHint == Integer.TYPE || - typeHint == ScriptRuntime.FloatClass || - typeHint == Float.TYPE || - typeHint == ScriptRuntime.DoubleClass || - typeHint == Double.TYPE) - { - hint = "number"; - } else { - throw Context.reportRuntimeError( - "Invalid JavaScript value of type " + - typeHint.toString()); - } - args[0] = hint; - } - Object v = ScriptableObject.getProperty(this, methodName); - if (!(v instanceof Function)) - continue; - Function fun = (Function) v; - Context cx = RhinoScriptEngine.enterContext(); - try { - v = fun.call(cx, fun.getParentScope(), this, args); - } finally { - cx.exit(); - } - if (v != null) { - if (!(v instanceof Scriptable)) { - return v; - } - if (typeHint == ScriptRuntime.ScriptableClass - || typeHint == ScriptRuntime.FunctionClass) - { - return v; - } - if (tryToString && v instanceof Wrapper) { - // Let a wrapped java.lang.String pass for a primitive - // string. - Object u = ((Wrapper)v).unwrap(); - if (u instanceof String) - return u; - } - } - } - // fall through to error - String arg = (typeHint == null) ? "undefined" : typeHint.getName(); - throw Context.reportRuntimeError( - "Cannot find default value for object " + arg); - } - - /** - * Implements the instanceof operator. - * - * @param instance The value that appeared on the LHS of the instanceof - * operator - * @return true if "this" appears in value's prototype chain - * - */ - public boolean hasInstance(Scriptable instance) { - // Default for JS objects (other than Function) is to do prototype - // chasing. - Scriptable proto = instance.getPrototype(); - while (proto != null) { - if (proto.equals(this)) return true; - proto = proto.getPrototype(); - } - return false; - } - - private String[] getAllKeys() { - ArrayList list = new ArrayList(); - synchronized (context) { - for (int scope : context.getScopes()) { - Bindings bindings = context.getBindings(scope); - if (bindings != null) { - list.ensureCapacity(bindings.size()); - for (String key : bindings.keySet()) { - list.add(key); - } - } - } - } - String[] res = new String[list.size()]; - list.toArray(res); - return res; - } - - /** - * We convert script values to the nearest Java value. - * We unwrap wrapped Java objects so that access from - * Bindings.get() would return "workable" value for Java. - * But, at the same time, we need to make few special cases - * and hence the following function is used. - */ - private Object jsToJava(Object jsObj) { - if (jsObj instanceof Wrapper) { - Wrapper njb = (Wrapper) jsObj; - /* importClass feature of ImporterTopLevel puts - * NativeJavaClass in global scope. If we unwrap - * it, importClass won't work. - */ - if (njb instanceof NativeJavaClass) { - return njb; - } - - /* script may use Java primitive wrapper type objects - * (such as java.lang.Integer, java.lang.Boolean etc) - * explicitly. If we unwrap, then these script objects - * will become script primitive types. For example, - * - * var x = new java.lang.Double(3.0); print(typeof x); - * - * will print 'number'. We don't want that to happen. - */ - Object obj = njb.unwrap(); - if (obj instanceof Number || obj instanceof String || - obj instanceof Boolean || obj instanceof Character) { - // special type wrapped -- we just leave it as is. - return njb; - } else { - // return unwrapped object for any other object. - return obj; - } - } else { // not-a-Java-wrapper - return jsObj; - } - } -}