Subject:
Embedding the shell
Date:
Wed, 8 Sep 1999 16:01:44 -0400
From:
"Kurt Westerfeld" <kurt@westerfeld.com>
To:
"Norris Boyd" <norris@netscape.com>
Norris, please find the attached zip file containing the (minor)
modifications to the Rhino shell module that enables the shell to be
embedded in a host application.
There are two areas of change to be concerned about:
1. Any and all references to System.in/out/err have been modified to use
Main.in/out/err, which default to System.in/out/err. Methods to do a setIn,
setOut, and setErr were added. Note that in/out/err on Main were made
static public, so that the jikes compiler wouldn't complain (I had them as
static protected, but when accessed outside of the package, a warning was
issued).
2. The global and sharedGlobal static variables were made protected so
that my app can make use of them (to add extensions after an initial pass
through main()).
That's it.
I have successfully used the facility to drop a remote telnet server into
the shell interpreter, effectively giving our server a remote shell
interpreter. It's quite nice, as we have a lot of extensions to Rhino
written that blend into our server already.
PS, I am still working on the array issues, but made a lot of progress
today. I just wanted to get this stuff off my desk.
Thanks!
________________________________________________________________________
Kurt Westerfeld
Managed Object Solutions
2722 Merrilee Drive
Suite 350
Fairfax, VA 22031
Phone: 703.208.3330 x225
Fax: 703.208.3331
http://www.mosol.com
mailto:kurt@mosol.com
shell.zip
Name:
shell.zip
Type:
Zip Compressed Data (application/x-zip-compressed)
Encoding:
base64
==============================================================================
Subject:
Rhino Array Source (Fixed)
Date:
Thu, 9 Sep 1999 14:12:03 -0400
From:
"Kurt Westerfeld" <kurt@mosol.com>
To:
"Norris Boyd" <norris@netscape.com>
Attached is NativeJavaObject.java, which seems to now pass the tests supplied to me by you and Scott. Not a lot of change, but a lot of
testing and thinking was involved. <g>
PS. I also fixed a bug in reportConversionError() which was throwing an IllegalArgumentException inside of the MessageFormat class at times.
It also looks a little nicer (uses formatting from NativeJavaMethod) and closer to the C implementation.
NativeJavaObject.java
Name:
NativeJavaObject.java
Type:
Java Source File (text/java)
Encoding:
quoted-printable
git-svn-id: svn://10.0.0.236/trunk@46585 18797224-902f-48f8-a5cc-f745e15eee43
879 lines
29 KiB
Java
879 lines
29 KiB
Java
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1997-1999 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
|
|
package org.mozilla.javascript;
|
|
|
|
import java.lang.reflect.*;
|
|
import java.util.Hashtable;
|
|
import java.util.Enumeration;
|
|
|
|
/**
|
|
* 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 {
|
|
|
|
public NativeJavaObject(Scriptable scope, Object javaObject,
|
|
JavaMembers members)
|
|
{
|
|
this.parent = scope;
|
|
this.javaObject = javaObject;
|
|
this.members = members;
|
|
}
|
|
|
|
public NativeJavaObject(Scriptable scope, Object javaObject,
|
|
Class staticType)
|
|
{
|
|
this.parent = scope;
|
|
this.javaObject = javaObject;
|
|
Class dynamicType = javaObject != null ? javaObject.getClass()
|
|
: staticType;
|
|
members = JavaMembers.lookupClass(scope, dynamicType, staticType);
|
|
fieldAndMethods = members.getFieldAndMethodsObjects(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) {
|
|
members.put(name, javaObject, value, false);
|
|
}
|
|
|
|
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 (javaObject.getClass() == ScriptRuntime.StringClass) {
|
|
return ScriptableObject.getClassPrototype(parent, "String");
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void setPrototype(Scriptable prototype) {
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
public static Object wrap(Scriptable scope, Object obj, Class staticType)
|
|
{
|
|
if (obj == null)
|
|
return obj;
|
|
Class cls = obj.getClass();
|
|
if (staticType != null && staticType.isPrimitive()) {
|
|
if (staticType == Void.TYPE)
|
|
return Undefined.instance;
|
|
if (staticType == Character.TYPE)
|
|
return new Integer((int) ((Character) obj).charValue());
|
|
return obj;
|
|
}
|
|
if (cls.isArray())
|
|
return NativeJavaArray.wrap(scope, obj);
|
|
if (obj instanceof Scriptable)
|
|
return obj;
|
|
if (Context.useJSObject && jsObjectClass != null &&
|
|
staticType != jsObjectClass && jsObjectClass.isInstance(obj))
|
|
{
|
|
try {
|
|
return jsObjectGetScriptable.invoke(obj, ScriptRuntime.emptyArgs);
|
|
}
|
|
catch (InvocationTargetException e) {
|
|
// Just abandon conversion from JSObject
|
|
}
|
|
catch (IllegalAccessException e) {
|
|
// Just abandon conversion from JSObject
|
|
}
|
|
}
|
|
return new NativeJavaObject(scope, obj, staticType);
|
|
}
|
|
|
|
public Object unwrap() {
|
|
return javaObject;
|
|
}
|
|
|
|
public String getClassName() {
|
|
return "JavaObject";
|
|
}
|
|
|
|
Function getConverter(String converterName) {
|
|
Object converterFunction = get(converterName, this);
|
|
if (converterFunction instanceof Function) {
|
|
return (Function) converterFunction;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
Object callConverter(Function converterFunction)
|
|
throws JavaScriptException
|
|
{
|
|
Function f = (Function) converterFunction;
|
|
return f.call(Context.getContext(), f.getParentScope(),
|
|
this, new Object[0]);
|
|
}
|
|
|
|
Object callConverter(String converterName)
|
|
throws JavaScriptException
|
|
{
|
|
Function converter = getConverter(converterName);
|
|
if (converter == null) {
|
|
Object[] errArgs = { converterName, javaObject.getClass().getName() };
|
|
throw Context.reportRuntimeError(
|
|
Context.getMessage("msg.java.conversion.implicit_method",
|
|
errArgs));
|
|
}
|
|
return callConverter(converter);
|
|
}
|
|
|
|
public Object getDefaultValue(Class hint) {
|
|
if (hint == null || hint == ScriptRuntime.StringClass)
|
|
return javaObject.toString();
|
|
try {
|
|
if (hint == ScriptRuntime.BooleanClass)
|
|
return callConverter("booleanValue");
|
|
if (hint == ScriptRuntime.NumberClass) {
|
|
return callConverter("doubleValue");
|
|
}
|
|
// fall through to error message
|
|
} catch (JavaScriptException jse) {
|
|
// fall through to error message
|
|
}
|
|
throw Context.reportRuntimeError(
|
|
Context.getMessage("msg.default.value", null));
|
|
}
|
|
|
|
|
|
/**
|
|
* 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 = NativeJavaObject.getConversionWeight(fromObj, to);
|
|
|
|
return (weight < CONVERSION_NONE);
|
|
}
|
|
|
|
static final int JSTYPE_UNDEFINED = 0; // undefined type
|
|
static final int JSTYPE_NULL = 1; // null
|
|
static final int JSTYPE_BOOLEAN = 2; // boolean
|
|
static final int JSTYPE_NUMBER = 3; // number
|
|
static final int JSTYPE_STRING = 4; // string
|
|
static final int JSTYPE_JAVA_CLASS = 5; // JavaClass
|
|
static final int JSTYPE_JAVA_OBJECT = 6; // JavaObject
|
|
static final int JSTYPE_JAVA_ARRAY = 7; // JavaArray
|
|
static final int JSTYPE_OBJECT = 8; // Scriptable
|
|
|
|
public static final byte CONVERSION_TRIVIAL = 1;
|
|
public static final byte CONVERSION_NONTRIVIAL = 0;
|
|
public 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>
|
|
*/
|
|
public static int getConversionWeight(Object fromObj, Class to) {
|
|
int fromCode = NativeJavaObject.getJSTypeCode(fromObj);
|
|
|
|
int result = CONVERSION_NONE;
|
|
|
|
switch (fromCode) {
|
|
|
|
case JSTYPE_UNDEFINED:
|
|
if (to == ScriptRuntime.StringClass ||
|
|
to == ScriptRuntime.ObjectClass) {
|
|
result = 1;
|
|
}
|
|
break;
|
|
|
|
case JSTYPE_NULL:
|
|
if (!to.isPrimitive()) {
|
|
result = 1;
|
|
}
|
|
break;
|
|
|
|
case JSTYPE_BOOLEAN:
|
|
// "boolean" is #1
|
|
if (to == Boolean.TYPE) {
|
|
result = 1;
|
|
}
|
|
else if (to == ScriptRuntime.BooleanClass) {
|
|
result = 2;
|
|
}
|
|
else if (to == ScriptRuntime.ObjectClass) {
|
|
result = 3;
|
|
}
|
|
else if (to == ScriptRuntime.StringClass) {
|
|
result = 4;
|
|
}
|
|
break;
|
|
|
|
case JSTYPE_NUMBER:
|
|
if (to.isPrimitive()) {
|
|
if (to == Double.TYPE) {
|
|
result = 1;
|
|
}
|
|
else if (to != Boolean.TYPE) {
|
|
result = 1 + NativeJavaObject.getSizeRank(to);
|
|
}
|
|
}
|
|
else {
|
|
if (to == ScriptRuntime.StringClass) {
|
|
// native numbers are #1-8
|
|
result = 9;
|
|
}
|
|
else if (to == ScriptRuntime.ObjectClass) {
|
|
result = 10;
|
|
}
|
|
else if (ScriptRuntime.NumberClass.isAssignableFrom(to)) {
|
|
// "double" is #1
|
|
result = 2;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case JSTYPE_STRING:
|
|
if (to == ScriptRuntime.StringClass) {
|
|
result = 1;
|
|
}
|
|
else if (to == ScriptRuntime.ObjectClass) {
|
|
result = 2;
|
|
}
|
|
else if (to.isPrimitive() && to != Boolean.TYPE) {
|
|
if (to == Character.TYPE) {
|
|
result = 3;
|
|
}
|
|
else {
|
|
result = 4;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case JSTYPE_JAVA_CLASS:
|
|
if (to == ScriptRuntime.ClassClass) {
|
|
result = 1;
|
|
}
|
|
else if (Context.useJSObject && jsObjectClass != null &&
|
|
jsObjectClass.isAssignableFrom(to)) {
|
|
result = 2;
|
|
}
|
|
else if (to == ScriptRuntime.ObjectClass) {
|
|
result = 3;
|
|
}
|
|
else if (to == ScriptRuntime.StringClass) {
|
|
result = 4;
|
|
}
|
|
break;
|
|
|
|
case JSTYPE_JAVA_OBJECT:
|
|
case JSTYPE_JAVA_ARRAY:
|
|
if (to == ScriptRuntime.StringClass) {
|
|
result = 2;
|
|
}
|
|
else if (to.isPrimitive() && to != Boolean.TYPE) {
|
|
result =
|
|
(fromCode == JSTYPE_JAVA_ARRAY) ?
|
|
CONVERSION_NONTRIVIAL :
|
|
2 + NativeJavaObject.getSizeRank(to);
|
|
}
|
|
else {
|
|
Object javaObj = fromObj;
|
|
if (javaObj instanceof NativeJavaObject) {
|
|
javaObj = ((NativeJavaObject)javaObj).unwrap();
|
|
}
|
|
if (to.isInstance(javaObj)) {
|
|
result = CONVERSION_NONTRIVIAL;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case JSTYPE_OBJECT:
|
|
// Other objects takes #1-#3 spots
|
|
if (Context.useJSObject && jsObjectClass != null &&
|
|
jsObjectClass.isAssignableFrom(to)) {
|
|
result = 1;
|
|
}
|
|
else if (fromObj instanceof NativeArray && to.isArray()) {
|
|
// This is a native array conversion to a java array
|
|
// Array conversions are all equal, and preferable to object
|
|
// and string conversion, per LC3.
|
|
result = 1;
|
|
}
|
|
else if (to == ScriptRuntime.ObjectClass) {
|
|
result = 2;
|
|
}
|
|
else if (to == ScriptRuntime.StringClass) {
|
|
result = 3;
|
|
}
|
|
else if (to.isPrimitive() || to != Boolean.TYPE) {
|
|
result = 3 + NativeJavaObject.getSizeRank(to);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
static int getJSTypeCode(Object value) {
|
|
if (value == null) {
|
|
return JSTYPE_NULL;
|
|
}
|
|
else if (value == Undefined.instance) {
|
|
return JSTYPE_UNDEFINED;
|
|
}
|
|
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 NativeJavaObject) {
|
|
return JSTYPE_JAVA_OBJECT;
|
|
}
|
|
else {
|
|
return JSTYPE_OBJECT;
|
|
}
|
|
}
|
|
else {
|
|
Class valueClass = value.getClass();
|
|
|
|
if (valueClass == ScriptRuntime.StringClass) {
|
|
return JSTYPE_STRING;
|
|
}
|
|
else if (valueClass == ScriptRuntime.BooleanClass) {
|
|
return JSTYPE_BOOLEAN;
|
|
}
|
|
else if (value instanceof Number) {
|
|
return JSTYPE_NUMBER;
|
|
}
|
|
else if (valueClass == ScriptRuntime.ClassClass) {
|
|
return JSTYPE_JAVA_CLASS;
|
|
}
|
|
else if (valueClass.isArray()) {
|
|
return JSTYPE_JAVA_ARRAY;
|
|
}
|
|
else {
|
|
return JSTYPE_JAVA_OBJECT;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Type-munging for field setting and method invocation.
|
|
* Conforms to LC3 specification
|
|
*/
|
|
public static Object coerceType(Class type, Object value) {
|
|
if (value != null && value.getClass() == type) {
|
|
return value;
|
|
}
|
|
|
|
switch (NativeJavaObject.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 == ScriptRuntime.ObjectClass) {
|
|
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 (Context.useJSObject && jsObjectClass != null &&
|
|
(type == ScriptRuntime.ObjectClass ||
|
|
jsObjectClass.isAssignableFrom(type))) {
|
|
return coerceToJSObject(type, (Scriptable)value);
|
|
}
|
|
else {
|
|
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 (type.isPrimitive()) {
|
|
if (type == Boolean.TYPE) {
|
|
reportConversionError(value, type);
|
|
}
|
|
return coerceToNumber(type, value);
|
|
}
|
|
else {
|
|
if (value instanceof Wrapper) {
|
|
value = ((Wrapper)value).unwrap();
|
|
}
|
|
if (type == ScriptRuntime.StringClass) {
|
|
return value.toString();
|
|
}
|
|
else {
|
|
if (type.isInstance(value)) {
|
|
return value;
|
|
}
|
|
else {
|
|
reportConversionError(value, type);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case JSTYPE_OBJECT:
|
|
if (Context.useJSObject && jsObjectClass != null &&
|
|
(type == ScriptRuntime.ObjectClass ||
|
|
jsObjectClass.isAssignableFrom(type))) {
|
|
return coerceToJSObject(type, (Scriptable)value);
|
|
}
|
|
else 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.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.jsGet_length();
|
|
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 {
|
|
reportConversionError(value, type);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
static Object coerceToJSObject(Class type, Scriptable value) {
|
|
// If JSObject compatibility is enabled, and the method wants it,
|
|
// wrap the Scriptable value in a JSObject.
|
|
|
|
if (ScriptRuntime.ScriptableClass.isAssignableFrom(type))
|
|
return value;
|
|
|
|
try {
|
|
Object ctorArgs[] = { value };
|
|
return jsObjectCtor.newInstance(ctorArgs);
|
|
} catch (InstantiationException instEx) {
|
|
throw new EvaluatorException("error generating JSObject wrapper for " +
|
|
value);
|
|
} catch (IllegalArgumentException argEx) {
|
|
throw new EvaluatorException("JSObject constructor doesn't want [Scriptable]!");
|
|
} catch (InvocationTargetException e) {
|
|
throw WrappedException.wrapException(e);
|
|
} catch (IllegalAccessException accessEx) {
|
|
throw new EvaluatorException("JSObject constructor is protected/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 < (double)Float.MIN_VALUE) {
|
|
return new Float((number > 0.0) ? +0.0 : -0.0);
|
|
}
|
|
else if (absNumber > (double)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 {
|
|
return new Long(toInteger(value,
|
|
ScriptRuntime.LongClass,
|
|
Long.MIN_VALUE,
|
|
Long.MAX_VALUE));
|
|
}
|
|
}
|
|
|
|
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));
|
|
}
|
|
|
|
|
|
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 {
|
|
double result = Double.NaN;
|
|
Method meth;
|
|
try {
|
|
meth = value.getClass().getMethod("doubleValue", null);
|
|
}
|
|
catch (NoSuchMethodException e) {
|
|
meth = null;
|
|
}
|
|
catch (SecurityException e) {
|
|
meth = null;
|
|
}
|
|
if (meth != null) {
|
|
try {
|
|
return ((Number)meth.invoke(value, 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());
|
|
}
|
|
}
|
|
|
|
static long toInteger(Object value, Class type, long min, long 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 < (double)min || d > (double)max) {
|
|
// Convert to string first, for more readable message
|
|
reportConversionError(ScriptRuntime.toString(value), type);
|
|
}
|
|
return (long)d;
|
|
}
|
|
|
|
static void reportConversionError(Object value, Class type) {
|
|
Object[] args = { Context.toString(value),
|
|
NativeJavaMethod.javaSignature(type)
|
|
};
|
|
throw Context.reportRuntimeError(
|
|
Context.getMessage("msg.conversion.not.allowed", args));
|
|
}
|
|
|
|
public static void initJSObject() {
|
|
if (!Context.useJSObject)
|
|
return;
|
|
// if netscape.javascript.JSObject is in the CLASSPATH, enable JSObject
|
|
// compatability wrappers
|
|
jsObjectClass = null;
|
|
try {
|
|
jsObjectClass = Class.forName("netscape.javascript.JSObject");
|
|
Class ctorParms[] = { ScriptRuntime.ScriptableClass };
|
|
jsObjectCtor = jsObjectClass.getConstructor(ctorParms);
|
|
jsObjectGetScriptable = jsObjectClass.getMethod("getScriptable",
|
|
new Class[0]);
|
|
} catch (ClassNotFoundException classEx) {
|
|
// jsObjectClass already null
|
|
} catch (NoSuchMethodException methEx) {
|
|
// jsObjectClass already null
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The parent scope of this object.
|
|
*/
|
|
protected Scriptable parent;
|
|
|
|
protected Object javaObject;
|
|
protected JavaMembers members;
|
|
private Hashtable fieldAndMethods;
|
|
static Class jsObjectClass;
|
|
static Constructor jsObjectCtor;
|
|
static Method jsObjectGetScriptable;
|
|
}
|
|
|