aboutsummaryrefslogtreecommitdiffstats
path: root/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaObject.java
diff options
context:
space:
mode:
Diffstat (limited to 'infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaObject.java')
-rw-r--r--infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaObject.java1002
1 files changed, 1002 insertions, 0 deletions
diff --git a/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaObject.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaObject.java
new file mode 100644
index 0000000..3d27852
--- /dev/null
+++ b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/NativeJavaObject.java
@@ -0,0 +1,1002 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Rhino code, released
+ * May 6, 1999.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1997-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Norris Boyd
+ * Igor Bukanov
+ * Frank Mitchell
+ * Mike Shaver
+ * Kemal Bayram
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * the GNU General Public License Version 2 or later (the "GPL"), in which
+ * case the provisions of the GPL are applicable instead of those above. If
+ * you wish to allow use of your version of this file only under the terms of
+ * the GPL and not to allow others to use your version of this file under the
+ * MPL, indicate your decision by deleting the provisions above and replacing
+ * them with the notice and other provisions required by the GPL. If you do
+ * not delete the provisions above, a recipient may use your version of this
+ * file under either the MPL or the GPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.javascript;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.Hashtable;
+import java.util.Date;
+
+/**
+ * This class reflects non-Array Java objects into the JavaScript environment. It
+ * reflect fields directly, and uses NativeJavaMethod objects to reflect (possibly
+ * overloaded) methods.<p>
+ *
+ * @author Mike Shaver
+ * @see NativeJavaArray
+ * @see NativeJavaPackage
+ * @see NativeJavaClass
+ */
+
+public class NativeJavaObject implements Scriptable, Wrapper, Serializable
+{
+ static final long serialVersionUID = -6948590651130498591L;
+
+ public NativeJavaObject() { }
+
+ public NativeJavaObject(Scriptable scope, Object javaObject,
+ Class staticType)
+ {
+ this(scope, javaObject, staticType, false);
+ }
+
+ public NativeJavaObject(Scriptable scope, Object javaObject,
+ Class staticType, boolean isAdapter)
+ {
+ this.parent = ScriptableObject.getVeryTopLevelScope(scope); // APPJET
+ this.javaObject = javaObject;
+ this.staticType = staticType;
+ this.isAdapter = isAdapter;
+ initMembers();
+ }
+
+ protected void initMembers() {
+ Class dynamicType;
+ if (javaObject != null) {
+ dynamicType = javaObject.getClass();
+ } else {
+ dynamicType = staticType;
+ }
+ members = JavaMembers.lookupClass(parent, dynamicType, staticType,
+ isAdapter);
+ fieldAndMethods
+ = members.getFieldAndMethodsObjects(this, javaObject, false);
+ }
+
+ public boolean has(String name, Scriptable start) {
+ return members.has(name, false);
+ }
+
+ public boolean has(int index, Scriptable start) {
+ return false;
+ }
+
+ public Object get(String name, Scriptable start) {
+ if (fieldAndMethods != null) {
+ Object result = fieldAndMethods.get(name);
+ if (result != null) {
+ return result;
+ }
+ }
+ // TODO: passing 'this' as the scope is bogus since it has
+ // no parent scope
+ return members.get(this, name, javaObject, false);
+ }
+
+ public Object get(int index, Scriptable start) {
+ throw members.reportMemberNotFound(Integer.toString(index));
+ }
+
+ public void put(String name, Scriptable start, Object value) {
+ // We could be asked to modify the value of a property in the
+ // prototype. Since we can't add a property to a Java object,
+ // we modify it in the prototype rather than copy it down.
+ if (prototype == null || members.has(name, false))
+ members.put(this, name, javaObject, value, false);
+ else
+ prototype.put(name, prototype, value);
+ }
+
+ public void put(int index, Scriptable start, Object value) {
+ throw members.reportMemberNotFound(Integer.toString(index));
+ }
+
+ public boolean hasInstance(Scriptable value) {
+ // This is an instance of a Java class, so always return false
+ return false;
+ }
+
+ public void delete(String name) {
+ }
+
+ public void delete(int index) {
+ }
+
+ public Scriptable getPrototype() {
+ if (prototype == null && javaObject instanceof String) {
+ return ScriptableObject.getClassPrototype(parent, "String");
+ }
+ return prototype;
+ }
+
+ /**
+ * Sets the prototype of the object.
+ */
+ public void setPrototype(Scriptable m) {
+ prototype = m;
+ }
+
+ /**
+ * Returns the parent (enclosing) scope of the object.
+ */
+ public Scriptable getParentScope() {
+ return parent;
+ }
+
+ /**
+ * Sets the parent (enclosing) scope of the object.
+ */
+ public void setParentScope(Scriptable m) {
+ parent = m;
+ }
+
+ public Object[] getIds() {
+ return members.getIds(false);
+ }
+
+/**
+@deprecated Use {@link Context#getWrapFactory()} together with calling {@link
+WrapFactory#wrap(Context, Scriptable, Object, Class)}
+*/
+ public static Object wrap(Scriptable scope, Object obj, Class staticType) {
+
+ Context cx = Context.getContext();
+ return cx.getWrapFactory().wrap(cx, scope, obj, staticType);
+ }
+
+ public Object unwrap() {
+ return javaObject;
+ }
+
+ public String getClassName() {
+ return "JavaObject";
+ }
+
+ public Object getDefaultValue(Class hint)
+ {
+ Object value;
+ if (hint == null) {
+ if (javaObject instanceof Boolean) {
+ hint = ScriptRuntime.BooleanClass;
+ }
+ }
+ if (hint == null || hint == ScriptRuntime.StringClass) {
+ value = javaObject.toString();
+ } else {
+ String converterName;
+ if (hint == ScriptRuntime.BooleanClass) {
+ converterName = "booleanValue";
+ } else if (hint == ScriptRuntime.NumberClass) {
+ converterName = "doubleValue";
+ } else {
+ throw Context.reportRuntimeError0("msg.default.value");
+ }
+ Object converterObject = get(converterName, this);
+ if (converterObject instanceof Function) {
+ Function f = (Function)converterObject;
+ value = f.call(Context.getContext(), f.getParentScope(),
+ this, ScriptRuntime.emptyArgs);
+ } else {
+ if (hint == ScriptRuntime.NumberClass
+ && javaObject instanceof Boolean)
+ {
+ boolean b = ((Boolean)javaObject).booleanValue();
+ value = ScriptRuntime.wrapNumber(b ? 1.0 : 0.0);
+ } else {
+ value = javaObject.toString();
+ }
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Determine whether we can/should convert between the given type and the
+ * desired one. This should be superceded by a conversion-cost calculation
+ * function, but for now I'll hide behind precedent.
+ */
+ public static boolean canConvert(Object fromObj, Class to) {
+ int weight = getConversionWeight(fromObj, to);
+
+ return (weight < CONVERSION_NONE);
+ }
+
+ private static final int JSTYPE_UNDEFINED = 0; // undefined type
+ private static final int JSTYPE_NULL = 1; // null
+ private static final int JSTYPE_BOOLEAN = 2; // boolean
+ private static final int JSTYPE_NUMBER = 3; // number
+ private static final int JSTYPE_STRING = 4; // string
+ private static final int JSTYPE_JAVA_CLASS = 5; // JavaClass
+ private static final int JSTYPE_JAVA_OBJECT = 6; // JavaObject
+ private static final int JSTYPE_JAVA_ARRAY = 7; // JavaArray
+ private static final int JSTYPE_OBJECT = 8; // Scriptable
+
+ static final byte CONVERSION_TRIVIAL = 1;
+ static final byte CONVERSION_NONTRIVIAL = 0;
+ static final byte CONVERSION_NONE = 99;
+
+ /**
+ * Derive a ranking based on how "natural" the conversion is.
+ * The special value CONVERSION_NONE means no conversion is possible,
+ * and CONVERSION_NONTRIVIAL signals that more type conformance testing
+ * is required.
+ * Based on
+ * <a href="http://www.mozilla.org/js/liveconnect/lc3_method_overloading.html">
+ * "preferred method conversions" from Live Connect 3</a>
+ */
+ static int getConversionWeight(Object fromObj, Class to) {
+ int fromCode = getJSTypeCode(fromObj);
+
+ switch (fromCode) {
+
+ case JSTYPE_UNDEFINED:
+ if (to == ScriptRuntime.StringClass ||
+ to == ScriptRuntime.ObjectClass) {
+ return 1;
+ }
+ break;
+
+ case JSTYPE_NULL:
+ if (!to.isPrimitive()) {
+ return 1;
+ }
+ break;
+
+ case JSTYPE_BOOLEAN:
+ // "boolean" is #1
+ if (to == Boolean.TYPE) {
+ return 1;
+ }
+ else if (to == ScriptRuntime.BooleanClass) {
+ return 2;
+ }
+ else if (to == ScriptRuntime.ObjectClass) {
+ return 3;
+ }
+ else if (to == ScriptRuntime.StringClass) {
+ return 4;
+ }
+ break;
+
+ case JSTYPE_NUMBER:
+ if (to.isPrimitive()) {
+ if (to == Double.TYPE) {
+ return 1;
+ }
+ else if (to != Boolean.TYPE) {
+ return 1 + getSizeRank(to);
+ }
+ }
+ else {
+ if (to == ScriptRuntime.StringClass) {
+ // native numbers are #1-8
+ return 9;
+ }
+ else if (to == ScriptRuntime.ObjectClass) {
+ return 10;
+ }
+ else if (ScriptRuntime.NumberClass.isAssignableFrom(to)) {
+ // "double" is #1
+ return 2;
+ }
+ }
+ break;
+
+ case JSTYPE_STRING:
+ if (to == ScriptRuntime.StringClass) {
+ return 1;
+ }
+ else if (to.isInstance(fromObj)) {
+ return 2;
+ }
+ else if (to.isPrimitive()) {
+ if (to == Character.TYPE) {
+ return 3;
+ } else if (to != Boolean.TYPE) {
+ return 4;
+ }
+ }
+ break;
+
+ case JSTYPE_JAVA_CLASS:
+ if (to == ScriptRuntime.ClassClass) {
+ return 1;
+ }
+ else if (to == ScriptRuntime.ObjectClass) {
+ return 3;
+ }
+ else if (to == ScriptRuntime.StringClass) {
+ return 4;
+ }
+ break;
+
+ case JSTYPE_JAVA_OBJECT:
+ case JSTYPE_JAVA_ARRAY:
+ Object javaObj = fromObj;
+ if (javaObj instanceof Wrapper) {
+ javaObj = ((Wrapper)javaObj).unwrap();
+ }
+ if (to.isInstance(javaObj)) {
+ return CONVERSION_NONTRIVIAL;
+ }
+ if (to == ScriptRuntime.StringClass) {
+ return 2;
+ }
+ else if (to.isPrimitive() && to != Boolean.TYPE) {
+ return (fromCode == JSTYPE_JAVA_ARRAY)
+ ? CONVERSION_NONE : 2 + getSizeRank(to);
+ }
+ break;
+
+ case JSTYPE_OBJECT:
+ // Other objects takes #1-#3 spots
+ if (to == fromObj.getClass()) {
+ // No conversion required
+ return 1;
+ }
+ if (to.isArray()) {
+ if (fromObj instanceof NativeArray) {
+ // This is a native array conversion to a java array
+ // Array conversions are all equal, and preferable to object
+ // and string conversion, per LC3.
+ return 1;
+ }
+ }
+ else if (to == ScriptRuntime.ObjectClass) {
+ return 2;
+ }
+ else if (to == ScriptRuntime.StringClass) {
+ return 3;
+ }
+ else if (to == ScriptRuntime.DateClass) {
+ if (fromObj instanceof NativeDate) {
+ // This is a native date to java date conversion
+ return 1;
+ }
+ }
+ else if (to.isInterface()) {
+ if (fromObj instanceof Function) {
+ // See comments in coerceType
+ if (to.getMethods().length == 1) {
+ return 1;
+ }
+ }
+ return 11;
+ }
+ else if (to.isPrimitive() && to != Boolean.TYPE) {
+ return 3 + getSizeRank(to);
+ }
+ break;
+ }
+
+ return CONVERSION_NONE;
+ }
+
+ static int getSizeRank(Class aType) {
+ if (aType == Double.TYPE) {
+ return 1;
+ }
+ else if (aType == Float.TYPE) {
+ return 2;
+ }
+ else if (aType == Long.TYPE) {
+ return 3;
+ }
+ else if (aType == Integer.TYPE) {
+ return 4;
+ }
+ else if (aType == Short.TYPE) {
+ return 5;
+ }
+ else if (aType == Character.TYPE) {
+ return 6;
+ }
+ else if (aType == Byte.TYPE) {
+ return 7;
+ }
+ else if (aType == Boolean.TYPE) {
+ return CONVERSION_NONE;
+ }
+ else {
+ return 8;
+ }
+ }
+
+ private static int getJSTypeCode(Object value) {
+ if (value == null) {
+ return JSTYPE_NULL;
+ }
+ else if (value == Undefined.instance) {
+ return JSTYPE_UNDEFINED;
+ }
+ else if (value instanceof String) {
+ return JSTYPE_STRING;
+ }
+ else if (value instanceof Number) {
+ return JSTYPE_NUMBER;
+ }
+ else if (value instanceof Boolean) {
+ return JSTYPE_BOOLEAN;
+ }
+ else if (value instanceof Scriptable) {
+ if (value instanceof NativeJavaClass) {
+ return JSTYPE_JAVA_CLASS;
+ }
+ else if (value instanceof NativeJavaArray) {
+ return JSTYPE_JAVA_ARRAY;
+ }
+ else if (value instanceof Wrapper) {
+ return JSTYPE_JAVA_OBJECT;
+ }
+ else {
+ return JSTYPE_OBJECT;
+ }
+ }
+ else if (value instanceof Class) {
+ return JSTYPE_JAVA_CLASS;
+ }
+ else {
+ Class valueClass = value.getClass();
+ if (valueClass.isArray()) {
+ return JSTYPE_JAVA_ARRAY;
+ }
+ else {
+ return JSTYPE_JAVA_OBJECT;
+ }
+ }
+ }
+
+ /**
+ * Not intended for public use. Callers should use the
+ * public API Context.toType.
+ * @deprecated as of 1.5 Release 4
+ * @see org.mozilla.javascript.Context#jsToJava(Object, Class)
+ */
+ public static Object coerceType(Class type, Object value)
+ {
+ return coerceTypeImpl(type, value);
+ }
+
+ /**
+ * Type-munging for field setting and method invocation.
+ * Conforms to LC3 specification
+ */
+ static Object coerceTypeImpl(Class type, Object value)
+ {
+ if (value != null && value.getClass() == type) {
+ return value;
+ }
+
+ switch (getJSTypeCode(value)) {
+
+ case JSTYPE_NULL:
+ // raise error if type.isPrimitive()
+ if (type.isPrimitive()) {
+ reportConversionError(value, type);
+ }
+ return null;
+
+ case JSTYPE_UNDEFINED:
+ if (type == ScriptRuntime.StringClass ||
+ type == ScriptRuntime.ObjectClass) {
+ return "undefined";
+ }
+ else {
+ reportConversionError("undefined", type);
+ }
+ break;
+
+ case JSTYPE_BOOLEAN:
+ // Under LC3, only JS Booleans can be coerced into a Boolean value
+ if (type == Boolean.TYPE ||
+ type == ScriptRuntime.BooleanClass ||
+ type == ScriptRuntime.ObjectClass) {
+ return value;
+ }
+ else if (type == ScriptRuntime.StringClass) {
+ return value.toString();
+ }
+ else {
+ reportConversionError(value, type);
+ }
+ break;
+
+ case JSTYPE_NUMBER:
+ if (type == ScriptRuntime.StringClass) {
+ return ScriptRuntime.toString(value);
+ }
+ else if (type == ScriptRuntime.ObjectClass) {
+ return coerceToNumber(Double.TYPE, value);
+ }
+ else if ((type.isPrimitive() && type != Boolean.TYPE) ||
+ ScriptRuntime.NumberClass.isAssignableFrom(type)) {
+ return coerceToNumber(type, value);
+ }
+ else {
+ reportConversionError(value, type);
+ }
+ break;
+
+ case JSTYPE_STRING:
+ if (type == ScriptRuntime.StringClass || type.isInstance(value)) {
+ return value;
+ }
+ else if (type == Character.TYPE
+ || type == ScriptRuntime.CharacterClass)
+ {
+ // Special case for converting a single char string to a
+ // character
+ // Placed here because it applies *only* to JS strings,
+ // not other JS objects converted to strings
+ if (((String)value).length() == 1) {
+ return new Character(((String)value).charAt(0));
+ }
+ else {
+ return coerceToNumber(type, value);
+ }
+ }
+ else if ((type.isPrimitive() && type != Boolean.TYPE)
+ || ScriptRuntime.NumberClass.isAssignableFrom(type))
+ {
+ return coerceToNumber(type, value);
+ }
+ else {
+ reportConversionError(value, type);
+ }
+ break;
+
+ case JSTYPE_JAVA_CLASS:
+ if (value instanceof Wrapper) {
+ value = ((Wrapper)value).unwrap();
+ }
+
+ if (type == ScriptRuntime.ClassClass ||
+ type == ScriptRuntime.ObjectClass) {
+ return value;
+ }
+ else if (type == ScriptRuntime.StringClass) {
+ return value.toString();
+ }
+ else {
+ reportConversionError(value, type);
+ }
+ break;
+
+ case JSTYPE_JAVA_OBJECT:
+ case JSTYPE_JAVA_ARRAY:
+ if (value instanceof Wrapper) {
+ value = ((Wrapper)value).unwrap();
+ }
+ if (type.isPrimitive()) {
+ if (type == Boolean.TYPE) {
+ reportConversionError(value, type);
+ }
+ return coerceToNumber(type, value);
+ }
+ else {
+ if (type == ScriptRuntime.StringClass) {
+ return value.toString();
+ }
+ else {
+ if (type.isInstance(value)) {
+ return value;
+ }
+ else {
+ reportConversionError(value, type);
+ }
+ }
+ }
+ break;
+
+ case JSTYPE_OBJECT:
+ if (type == ScriptRuntime.StringClass) {
+ return ScriptRuntime.toString(value);
+ }
+ else if (type.isPrimitive()) {
+ if (type == Boolean.TYPE) {
+ reportConversionError(value, type);
+ }
+ return coerceToNumber(type, value);
+ }
+ else if (type.isInstance(value)) {
+ return value;
+ }
+ else if (type == ScriptRuntime.DateClass
+ && value instanceof NativeDate)
+ {
+ double time = ((NativeDate)value).getJSTimeValue();
+ // XXX: This will replace NaN by 0
+ return new Date((long)time);
+ }
+ else if (type.isArray() && value instanceof NativeArray) {
+ // Make a new java array, and coerce the JS array components
+ // to the target (component) type.
+ NativeArray array = (NativeArray) value;
+ long length = array.getLength();
+ Class arrayType = type.getComponentType();
+ Object Result = Array.newInstance(arrayType, (int)length);
+ for (int i = 0 ; i < length ; ++i) {
+ try {
+ Array.set(Result, i, coerceType(arrayType,
+ array.get(i, array)));
+ }
+ catch (EvaluatorException ee) {
+ reportConversionError(value, type);
+ }
+ }
+
+ return Result;
+ }
+ else if (value instanceof Wrapper) {
+ value = ((Wrapper)value).unwrap();
+ if (type.isInstance(value))
+ return value;
+ reportConversionError(value, type);
+ }
+ else if (type.isInterface() && value instanceof Callable) {
+ // Try to use function as implementation of Java interface.
+ //
+ // XXX: Curently only instances of ScriptableObject are
+ // supported since the resulting interface proxies should
+ // be reused next time conversion is made and generic
+ // Callable has no storage for it. Weak references can
+ // address it but for now use this restriction.
+ if (value instanceof ScriptableObject) {
+ ScriptableObject so = (ScriptableObject)value;
+ Object key = Kit.makeHashKeyFromPair(
+ COERCED_INTERFACE_KEY, type);
+ Object old = so.getAssociatedValue(key);
+ if (old != null) {
+ // Function was already wrapped
+ return old;
+ }
+ Context cx = Context.getContext();
+ Object glue
+ = InterfaceAdapter.create(cx, type, (Callable)value);
+ // Store for later retrival
+ glue = so.associateValue(key, glue);
+ return glue;
+ }
+ reportConversionError(value, type);
+ } else {
+ reportConversionError(value, type);
+ }
+ break;
+ }
+
+ return value;
+ }
+
+ private static Object coerceToNumber(Class type, Object value)
+ {
+ Class valueClass = value.getClass();
+
+ // Character
+ if (type == Character.TYPE || type == ScriptRuntime.CharacterClass) {
+ if (valueClass == ScriptRuntime.CharacterClass) {
+ return value;
+ }
+ return new Character((char)toInteger(value,
+ ScriptRuntime.CharacterClass,
+ Character.MIN_VALUE,
+ Character.MAX_VALUE));
+ }
+
+ // Double, Float
+ if (type == ScriptRuntime.ObjectClass ||
+ type == ScriptRuntime.DoubleClass || type == Double.TYPE) {
+ return valueClass == ScriptRuntime.DoubleClass
+ ? value
+ : new Double(toDouble(value));
+ }
+
+ if (type == ScriptRuntime.FloatClass || type == Float.TYPE) {
+ if (valueClass == ScriptRuntime.FloatClass) {
+ return value;
+ }
+ else {
+ double number = toDouble(value);
+ if (Double.isInfinite(number) || Double.isNaN(number)
+ || number == 0.0) {
+ return new Float((float)number);
+ }
+ else {
+ double absNumber = Math.abs(number);
+ if (absNumber < Float.MIN_VALUE) {
+ return new Float((number > 0.0) ? +0.0 : -0.0);
+ }
+ else if (absNumber > Float.MAX_VALUE) {
+ return new Float((number > 0.0) ?
+ Float.POSITIVE_INFINITY :
+ Float.NEGATIVE_INFINITY);
+ }
+ else {
+ return new Float((float)number);
+ }
+ }
+ }
+ }
+
+ // Integer, Long, Short, Byte
+ if (type == ScriptRuntime.IntegerClass || type == Integer.TYPE) {
+ if (valueClass == ScriptRuntime.IntegerClass) {
+ return value;
+ }
+ else {
+ return new Integer((int)toInteger(value,
+ ScriptRuntime.IntegerClass,
+ Integer.MIN_VALUE,
+ Integer.MAX_VALUE));
+ }
+ }
+
+ if (type == ScriptRuntime.LongClass || type == Long.TYPE) {
+ if (valueClass == ScriptRuntime.LongClass) {
+ return value;
+ } else {
+ /* Long values cannot be expressed exactly in doubles.
+ * We thus use the largest and smallest double value that
+ * has a value expressible as a long value. We build these
+ * numerical values from their hexidecimal representations
+ * to avoid any problems caused by attempting to parse a
+ * decimal representation.
+ */
+ final double max = Double.longBitsToDouble(0x43dfffffffffffffL);
+ final double min = Double.longBitsToDouble(0xc3e0000000000000L);
+ return new Long(toInteger(value,
+ ScriptRuntime.LongClass,
+ min,
+ max));
+ }
+ }
+
+ if (type == ScriptRuntime.ShortClass || type == Short.TYPE) {
+ if (valueClass == ScriptRuntime.ShortClass) {
+ return value;
+ }
+ else {
+ return new Short((short)toInteger(value,
+ ScriptRuntime.ShortClass,
+ Short.MIN_VALUE,
+ Short.MAX_VALUE));
+ }
+ }
+
+ if (type == ScriptRuntime.ByteClass || type == Byte.TYPE) {
+ if (valueClass == ScriptRuntime.ByteClass) {
+ return value;
+ }
+ else {
+ return new Byte((byte)toInteger(value,
+ ScriptRuntime.ByteClass,
+ Byte.MIN_VALUE,
+ Byte.MAX_VALUE));
+ }
+ }
+
+ return new Double(toDouble(value));
+ }
+
+
+ private static double toDouble(Object value)
+ {
+ if (value instanceof Number) {
+ return ((Number)value).doubleValue();
+ }
+ else if (value instanceof String) {
+ return ScriptRuntime.toNumber((String)value);
+ }
+ else if (value instanceof Scriptable) {
+ if (value instanceof Wrapper) {
+ // XXX: optimize tail-recursion?
+ return toDouble(((Wrapper)value).unwrap());
+ }
+ else {
+ return ScriptRuntime.toNumber(value);
+ }
+ }
+ else {
+ Method meth;
+ try {
+ meth = value.getClass().getMethod("doubleValue",
+ (Class [])null);
+ }
+ catch (NoSuchMethodException e) {
+ meth = null;
+ }
+ catch (SecurityException e) {
+ meth = null;
+ }
+ if (meth != null) {
+ try {
+ return ((Number)meth.invoke(value,
+ (Object [])null)).doubleValue();
+ }
+ catch (IllegalAccessException e) {
+ // XXX: ignore, or error message?
+ reportConversionError(value, Double.TYPE);
+ }
+ catch (InvocationTargetException e) {
+ // XXX: ignore, or error message?
+ reportConversionError(value, Double.TYPE);
+ }
+ }
+ return ScriptRuntime.toNumber(value.toString());
+ }
+ }
+
+ private static long toInteger(Object value, Class type,
+ double min, double max)
+ {
+ double d = toDouble(value);
+
+ if (Double.isInfinite(d) || Double.isNaN(d)) {
+ // Convert to string first, for more readable message
+ reportConversionError(ScriptRuntime.toString(value), type);
+ }
+
+ if (d > 0.0) {
+ d = Math.floor(d);
+ }
+ else {
+ d = Math.ceil(d);
+ }
+
+ if (d < min || d > max) {
+ // Convert to string first, for more readable message
+ reportConversionError(ScriptRuntime.toString(value), type);
+ }
+ return (long)d;
+ }
+
+ static void reportConversionError(Object value, Class type)
+ {
+ // It uses String.valueOf(value), not value.toString() since
+ // value can be null, bug 282447.
+ throw Context.reportRuntimeError2(
+ "msg.conversion.not.allowed",
+ String.valueOf(value),
+ JavaMembers.javaSignature(type));
+ }
+
+ private void writeObject(ObjectOutputStream out)
+ throws IOException
+ {
+ out.defaultWriteObject();
+
+ out.writeBoolean(isAdapter);
+ if (isAdapter) {
+ if (adapter_writeAdapterObject == null) {
+ throw new IOException();
+ }
+ Object[] args = { javaObject, out };
+ try {
+ adapter_writeAdapterObject.invoke(null, args);
+ } catch (Exception ex) {
+ throw new IOException();
+ }
+ } else {
+ out.writeObject(javaObject);
+ }
+
+ if (staticType != null) {
+ out.writeObject(staticType.getClass().getName());
+ } else {
+ out.writeObject(null);
+ }
+ }
+
+ private void readObject(ObjectInputStream in)
+ throws IOException, ClassNotFoundException
+ {
+ in.defaultReadObject();
+
+ isAdapter = in.readBoolean();
+ if (isAdapter) {
+ if (adapter_readAdapterObject == null)
+ throw new ClassNotFoundException();
+ Object[] args = { this, in };
+ try {
+ javaObject = adapter_readAdapterObject.invoke(null, args);
+ } catch (Exception ex) {
+ throw new IOException();
+ }
+ } else {
+ javaObject = in.readObject();
+ }
+
+ String className = (String)in.readObject();
+ if (className != null) {
+ staticType = Class.forName(className);
+ } else {
+ staticType = null;
+ }
+
+ initMembers();
+ }
+
+ /**
+ * The prototype of this object.
+ */
+ protected Scriptable prototype;
+
+ /**
+ * The parent scope of this object.
+ */
+ protected Scriptable parent;
+
+ protected transient Object javaObject;
+
+ protected transient Class staticType;
+ protected transient JavaMembers members;
+ private transient Hashtable fieldAndMethods;
+ private transient boolean isAdapter;
+
+ private static final Object COERCED_INTERFACE_KEY = new Object();
+ private static Method adapter_writeAdapterObject;
+ private static Method adapter_readAdapterObject;
+
+ static {
+ // Reflection in java is verbose
+ Class[] sig2 = new Class[2];
+ Class cl = Kit.classOrNull("org.mozilla.javascript.JavaAdapter");
+ if (cl != null) {
+ try {
+ sig2[0] = ScriptRuntime.ObjectClass;
+ sig2[1] = Kit.classOrNull("java.io.ObjectOutputStream");
+ adapter_writeAdapterObject = cl.getMethod("writeAdapterObject",
+ sig2);
+
+ sig2[0] = ScriptRuntime.ScriptableClass;
+ sig2[1] = Kit.classOrNull("java.io.ObjectInputStream");
+ adapter_readAdapterObject = cl.getMethod("readAdapterObject",
+ sig2);
+
+ } catch (Exception ex) {
+ adapter_writeAdapterObject = null;
+ adapter_readAdapterObject = null;
+ }
+ }
+ }
+
+}