--- a/jdk/src/share/classes/com/sun/script/javascript/ExternalScriptable.java Mon May 13 12:26:28 2013 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -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<Object, Object> indexedProps;
-
- // my prototype
- private Scriptable prototype;
- // my parent scope, if any
- private Scriptable parent;
-
- ExternalScriptable(ScriptContext context) {
- this(context, new HashMap<Object, Object>());
- }
-
- ExternalScriptable(ScriptContext context, Map<Object, Object> 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. <p>
- *
- * A <code>hint</code> 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<String> list = new ArrayList<String>();
- 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;
- }
- }
-}