Fixes for LC3 regression tests, including:

- check static members of instances in JavaMembers.put

- do not unwrap Wrappers before calling NativeJavaMethod.findFunction
     or NativeJavaObject.coerceType; both methods may need extra information
     provided by the wrapper.

- separate Java signatures for resolving overloaded methods and script
     signatures for error messages, so we can distinguish primitive types
     from classes.

- separate Java signatures for resolving overloaded methods and script
     signatures for error messages, so we can distinguish primitive types
     from classes.

- prevent a NativeJavaClass from being treated as a wrapped instance of
     java.lang.Class

- correct bug which preferred the *less* specific of two classes in
     NativeJavaMethod.preferSignature

- add new LC3 conversion rules to  NativeJavaObject.coerceTypes.

- coerce JS numbers to Java numbers or chars only if the JS number is in
     range.
This commit is contained in:
frankm%eng.sun.com 1999-06-11 01:24:40 +00:00
parent 52424b1aea
commit a66a71b8d7
12 changed files with 530 additions and 204 deletions

View File

@ -166,6 +166,10 @@ class JavaMembers {
{
Hashtable ht = isStatic ? staticMembers : members;
Object member = ht.get(name);
if (!isStatic && member == null) {
// Try to get static member from instance (LC3)
member = staticMembers.get(name);
}
if (member == null)
throw reportMemberNotFound(name);
if (member instanceof FieldAndMethods) {
@ -174,9 +178,6 @@ class JavaMembers {
Field field = null;
try {
field = (Field) member;
// XXX what was this for?
//if (obj instanceof Wrapper)
// obj = ((Wrapper)obj).unwrap();
field.set(javaObject, NativeJavaObject.coerceType(field.getType(),
value));
} catch (ClassCastException e) {

View File

@ -82,8 +82,6 @@ public class NativeJavaArray extends NativeJavaObject {
public void put(int index, Scriptable start, Object value) {
if (0 <= index && index < length) {
if (value instanceof Wrapper)
value = ((Wrapper)value).unwrap();
Array.set(array, index, NativeJavaObject.coerceType(cls, value));
return;
}

View File

@ -129,16 +129,11 @@ public class NativeJavaClass extends NativeJavaObject implements Function {
if (! (Modifier.isInterface(modifiers) ||
Modifier.isAbstract(modifiers)))
{
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Wrapper)
args[i] = ((Wrapper)args[i]).unwrap();
}
Constructor[] ctors = members.getConstructors();
Member member = NativeJavaMethod.findFunction(ctors, args);
Constructor ctor = (Constructor) member;
if (ctor == null) {
String sig = NativeJavaMethod.signature(args);
String sig = NativeJavaMethod.scriptSignature(args);
Object errArgs[] = { classObject.getName(), sig };
throw Context.reportRuntimeError(Context.getMessage(
"msg.no.java.ctor", errArgs));
@ -187,8 +182,8 @@ public class NativeJavaClass extends NativeJavaObject implements Function {
args[i] = NativeJavaObject.coerceType(paramTypes[i], args[i]);
}
try {
/* we need to force this to be wrapped, because construct _has_
* to return a scriptable */
// we need to force this to be wrapped, because construct _has_
// to return a scriptable
return
(Scriptable) NativeJavaObject.wrap(topLevel,
ctor.newInstance(args),
@ -201,7 +196,7 @@ public class NativeJavaClass extends NativeJavaObject implements Function {
("msg.cant.instantiate",
errArgs));
} catch (IllegalArgumentException argEx) {
String signature = NativeJavaMethod.signature(args);
String signature = NativeJavaMethod.scriptSignature(args);
String ctorString = ctor.toString();
Object[] errArgs = { argEx.getMessage(),ctorString,signature };
throw Context.reportRuntimeError(Context.getMessage
@ -222,11 +217,16 @@ public class NativeJavaClass extends NativeJavaObject implements Function {
/**
* Determines if prototype is a wrapped Java object and performs
* a Java "instanceof"
* a Java "instanceof".
* Exception: if value is an instance of NativeJavaClass, it isn't
* considered an instance of the Java class; this forestalls any
* name conflicts between java.lang.Class's methods and the
* static methods exposed by a JavaNativeClass.
*/
public boolean hasInstance(Scriptable value) {
if (value instanceof NativeJavaObject) {
if (value instanceof NativeJavaObject &&
!(value instanceof NativeJavaClass)) {
Object instance = ((NativeJavaObject)value).unwrap();
return getClassObject().isInstance(instance);

View File

@ -67,55 +67,58 @@ public class NativeJavaMethod extends NativeFunction implements Function {
methods = newMeths;
}
static String signature(Class type) {
if (type == null)
static String scriptSignature(Object value) {
if (value == null) {
return "null";
if (type == ScriptRuntime.BooleanClass)
return "boolean";
if (type == ScriptRuntime.ByteClass)
return "byte";
if (type == ScriptRuntime.ShortClass)
return "short";
if (type == ScriptRuntime.IntegerClass)
return "int";
if (type == ScriptRuntime.LongClass)
return "long";
if (type == ScriptRuntime.FloatClass)
return "float";
if (type == ScriptRuntime.DoubleClass)
return "double";
if (type == ScriptRuntime.StringClass)
return "string";
if (ScriptRuntime.ScriptableClass.isAssignableFrom(type)) {
if (ScriptRuntime.FunctionClass.isAssignableFrom(type))
return "function";
}
else {
Class type = value.getClass();
if (type == ScriptRuntime.UndefinedClass)
return "undefined";
return "object";
if (type == ScriptRuntime.BooleanClass)
return "boolean";
if (type == ScriptRuntime.StringClass)
return "string";
if (ScriptRuntime.NumberClass.isAssignableFrom(type))
return "number";
if (value instanceof NativeJavaObject) {
return ((NativeJavaObject)value).unwrap().getClass().getName();
}
if (value instanceof Scriptable) {
if (value instanceof Function)
return "function";
return "object";
}
return javaSignature(type);
}
if (type.isArray()) {
return signature(type.getComponentType()) + "[]";
}
return type.getName();
}
static String signature(Object[] values) {
static String scriptSignature(Object[] values) {
StringBuffer sig = new StringBuffer();
for (int i = 0; i < values.length; i++) {
if (i != 0)
sig.append(',');
sig.append(values[i] == null ? "null"
: signature(values[i].getClass()));
sig.append(scriptSignature(values[i]));
}
return sig.toString();
}
static String signature(Class[] types) {
static String javaSignature(Class type) {
if (type == null) {
return "null";
}
else if (type.isArray()) {
return javaSignature(type.getComponentType()) + "[]";
}
return type.getName();
}
static String javaSignature(Class[] types) {
StringBuffer sig = new StringBuffer();
for (int i = 0; i < types.length; i++) {
if (i != 0)
sig.append(',');
sig.append(signature(types[i]));
sig.append(javaSignature(types[i]));
}
return sig.toString();
}
@ -125,11 +128,11 @@ public class NativeJavaMethod extends NativeFunction implements Function {
if (member instanceof Method) {
paramTypes = ((Method) member).getParameterTypes();
return member.getName() + "(" + signature(paramTypes) + ")";
return member.getName() + "(" + javaSignature(paramTypes) + ")";
}
else {
paramTypes = ((Constructor) member).getParameterTypes();
return "(" + signature(paramTypes) + ")";
return "(" + javaSignature(paramTypes) + ")";
}
}
@ -142,16 +145,11 @@ public class NativeJavaMethod extends NativeFunction implements Function {
throw new RuntimeException("No methods defined for call");
}
// Eliminate useless args[0] and unwrap if required
for (int i = 0; i < args.length; i++)
if (args[i] instanceof Wrapper)
args[i] = ((Wrapper)args[i]).unwrap();
Method meth = (Method) findFunction(methods, args);
if (meth == null) {
Class c = methods[0].getDeclaringClass();
String sig = c.getName() + "." + names[0] + "(" +
signature(args) + ")";
scriptSignature(args) + ")";
Object errArgs[] = { sig };
throw Context.reportRuntimeError(
Context.getMessage("msg.java.no_such_method", errArgs));
@ -179,9 +177,28 @@ public class NativeJavaMethod extends NativeFunction implements Function {
}
}
try {
if (debug) {
printDebug("Calling", meth, args);
}
Object retval = meth.invoke(javaObject, args);
Class staticType = meth.getReturnType();
if (debug) {
Class actualType = (retval == null) ? null : retval.getClass();
System.err.println(" ----- Returned " + retval +
" actual = " + actualType +
" expect = " + staticType);
}
Object wrapped = NativeJavaObject.wrap(scope, retval, staticType);
if (debug) {
Class actualType = (wrapped == null) ? null : wrapped.getClass();
System.err.println(" ----- Wrapped as " + wrapped +
" class = " + actualType);
}
// XXX set prototype && parent
if (wrapped == Undefined.instance)
return wrapped;
@ -202,12 +219,6 @@ public class NativeJavaMethod extends NativeFunction implements Function {
}
}
/*
public Object getDefaultValue(Class hint) {
return this;
}
*/
/**
* Find the correct function to call given the set of methods
* or constructors and the arguments.
@ -339,7 +350,7 @@ public class NativeJavaMethod extends NativeFunction implements Function {
if (isCtor) {
Object errArgs[] = {
bestFit.getName(),
NativeJavaMethod.signature(args),
NativeJavaMethod.scriptSignature(args),
buf.toString()
};
errMsg =
@ -349,7 +360,7 @@ public class NativeJavaMethod extends NativeFunction implements Function {
Object errArgs[] = {
bestFit.getDeclaringClass().getName(),
bestFit.getName(),
NativeJavaMethod.signature(args),
NativeJavaMethod.scriptSignature(args),
buf.toString()
};
errMsg = Context.getMessage("msg.method.ambiguous", errArgs);
@ -417,10 +428,10 @@ public class NativeJavaMethod extends NativeFunction implements Function {
rank2 == NativeJavaObject.CONVERSION_NONTRIVIAL) {
if (toClass1.isAssignableFrom(toClass2)) {
return PREFERENCE_FIRST_ARG;
return PREFERENCE_SECOND_ARG;
}
else if (toClass2.isAssignableFrom(toClass1)) {
return PREFERENCE_SECOND_ARG;
return PREFERENCE_FIRST_ARG;
}
}
else {
@ -445,7 +456,7 @@ public class NativeJavaMethod extends NativeFunction implements Function {
System.err.println(" ----- " + msg +
member.getDeclaringClass().getName() +
"." + signature(member) +
" for arguments (" + signature(args) + ")");
" for arguments (" + scriptSignature(args) + ")");
}
}

View File

@ -219,7 +219,7 @@ public class NativeJavaObject implements Scriptable, Wrapper {
public static boolean canConvert(Object fromObj, Class to) {
int weight = NativeJavaObject.getConversionWeight(fromObj, to);
return (weight != CONVERSION_NONE);
return (weight < CONVERSION_NONE);
}
static final int JSTYPE_UNDEFINED = 0; // undefined type
@ -286,10 +286,7 @@ public class NativeJavaObject implements Scriptable, Wrapper {
if (to == Double.TYPE) {
result = 1;
}
else if (to == Boolean.TYPE) {
result = CONVERSION_NONE;
}
else {
else if (to != Boolean.TYPE) {
result = 1 + NativeJavaObject.getSizeRank(to);
}
}
@ -315,7 +312,7 @@ public class NativeJavaObject implements Scriptable, Wrapper {
else if (to == ScriptRuntime.ObjectClass) {
result = 2;
}
else if (to.isPrimitive()) {
else if (to.isPrimitive() && to != Boolean.TYPE) {
if (to == Character.TYPE) {
result = 3;
}
@ -329,7 +326,7 @@ public class NativeJavaObject implements Scriptable, Wrapper {
if (to == ScriptRuntime.ClassClass) {
result = 1;
}
if (Context.useJSObject && jsObjectClass != null &&
else if (Context.useJSObject && jsObjectClass != null &&
jsObjectClass.isAssignableFrom(to)) {
result = 2;
}
@ -346,7 +343,7 @@ public class NativeJavaObject implements Scriptable, Wrapper {
if (to == ScriptRuntime.StringClass) {
result = 2;
}
else if (to.isPrimitive()) {
else if (to.isPrimitive() && to != Boolean.TYPE) {
result =
(fromCode == JSTYPE_JAVA_ARRAY) ?
CONVERSION_NONTRIVIAL :
@ -375,7 +372,7 @@ public class NativeJavaObject implements Scriptable, Wrapper {
else if (to == ScriptRuntime.StringClass) {
result = 3;
}
else if (to.isPrimitive()) {
else if (to.isPrimitive() || to != Boolean.TYPE) {
result = 3 + NativeJavaObject.getSizeRank(to);
}
break;
@ -407,6 +404,9 @@ public class NativeJavaObject implements Scriptable, Wrapper {
else if (aType == Byte.TYPE) {
return 7;
}
else if (aType == Boolean.TYPE) {
return CONVERSION_NONE;
}
else {
return 8;
}
@ -426,6 +426,9 @@ public class NativeJavaObject implements Scriptable, Wrapper {
else if (value instanceof NativeJavaArray) {
return JSTYPE_JAVA_ARRAY;
}
else if (value instanceof NativeString) {
return JSTYPE_STRING;
}
else if (value instanceof NativeJavaObject) {
return JSTYPE_JAVA_OBJECT;
}
@ -459,14 +462,41 @@ public class NativeJavaObject implements Scriptable, Wrapper {
/**
* Type-munging for field setting and method invocation.
* Conforms to LC3 specification
*/
public static Object coerceType(Class type, Object value) {
// Don't coerce null to a string (or other object)
if (value == null) {
// raise error if type.isPrimitive()
if (type.isPrimitive()) {
reportConversionError(value, type);
}
return null;
}
else if (value == Undefined.instance) {
if (type == ScriptRuntime.StringClass ||
type == ScriptRuntime.ObjectClass) {
return "undefined";
}
else {
// report conversion error
reportConversionError("undefined", type);
}
}
// For ScriptRuntime.finalClasses we can compare valueClass to ScriptRuntime.aClass object
// Special case: converting JS numbers to Objects
// Done before we unwrap, to distinguish from a
// wrapped java.lang.Number.
if (type == ScriptRuntime.ObjectClass && value instanceof Number) {
return new Double(((Number)value).doubleValue());
}
// Unwrap at this point; callers do not need to unwrap
if (value instanceof Wrapper) {
value = ((Wrapper)value).unwrap();
}
// For final classes we can compare valueClass to a Class object
// rather than using instanceof
Class valueClass = value.getClass();
@ -479,29 +509,43 @@ public class NativeJavaObject implements Scriptable, Wrapper {
return ScriptRuntime.toString(value);
// Boolean
if (type == Boolean.TYPE || type == ScriptRuntime.BooleanClass)
return new Boolean(ScriptRuntime.toBoolean(value));
if (type == Boolean.TYPE || type == ScriptRuntime.BooleanClass) {
// Under LC3, only JS Booleans can be coerced into a Boolean value
if (valueClass == ScriptRuntime.BooleanClass) {
return value;
}
else {
reportConversionError(value, type);
}
}
// Character
if (type == Character.TYPE || type == ScriptRuntime.CharacterClass) {
// Special case for converting a single char string to a character
if (valueClass == ScriptRuntime.StringClass && ((String) value).length() == 1)
return new Character(((String) value).charAt(0));
return new Character((char)ScriptRuntime.toInteger(value));
/*
if (valueClass == ScriptRuntime.StringClass) {
String string = (String)value;
if (string.length() == 1) {
char ch = string.charAt(0);
// XXX: Next test not in LC3 spec, but is backwardly
// compatible and satisfies regression tests
if (!Character.isDigit(ch)) {
return new Character(ch);
}
}
}
*/
if (valueClass == ScriptRuntime.CharacterClass) {
return value;
}
return new Character((char)toInteger(value,
ScriptRuntime.CharacterClass,
Character.MIN_VALUE,
Character.MAX_VALUE));
}
// Integer, Long, Char, Byte
if (type == ScriptRuntime.IntegerClass || type == Integer.TYPE)
return new Integer((int)ScriptRuntime.toInteger(value));
if (type == ScriptRuntime.LongClass || type == Long.TYPE)
return new Long((long)ScriptRuntime.toInteger(value));
if (type == ScriptRuntime.ByteClass || type == Byte.TYPE)
return new Byte((byte)ScriptRuntime.toInteger(value));
if (type == ScriptRuntime.ShortClass || type == Short.TYPE)
return new Short((short)ScriptRuntime.toInteger(value));
// Double, Float
if (type == ScriptRuntime.DoubleClass || type == Double.TYPE) {
return valueClass == ScriptRuntime.DoubleClass
@ -510,14 +554,81 @@ public class NativeJavaObject implements Scriptable, Wrapper {
}
if (type == ScriptRuntime.FloatClass || type == Float.TYPE) {
return valueClass == ScriptRuntime.FloatClass
? value
: new Float(ScriptRuntime.toNumber(value));
if (valueClass == ScriptRuntime.FloatClass) {
return value;
}
else {
double number = ScriptRuntime.toNumber(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));
}
}
if (valueClass == ScriptRuntime.DoubleClass)
return value;
// If JSObject compatibility is enabled, and the method wants it,
// wrap the Scriptable value in a JSObject.
if (Context.useJSObject && jsObjectClass != null &&
@ -540,12 +651,50 @@ public class NativeJavaObject implements Scriptable, Wrapper {
}
}
if (ScriptRuntime.NumberClass.isInstance(value))
return new Double(((Number) value).doubleValue());
return value;
}
static long toInteger(Object value, Class type, long min, long max) {
double d;
if (value instanceof Number) {
d = ((Number)value).doubleValue();
}
else if (value instanceof String) {
d = ScriptRuntime.toNumber((String)value);
}
else if (value instanceof Scriptable) {
d = ScriptRuntime.toNumber(value);
}
else {
// XXX: is this correct?
d = ScriptRuntime.toNumber(value.toString());
}
if (Double.isInfinite(d) || Double.isNaN(d)) {
// Convert to string first, for more readable message
reportConversionError(value.toString(), 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(value.toString(), type);
}
return (long)d;
}
static void reportConversionError(Object value, Class type) {
Object[] args = {value, type};
throw Context.reportRuntimeError(Context.getMessage("msg.conversion.not.allowed", args));
}
public static void initJSObject() {
if (!Context.useJSObject)
return;

View File

@ -138,6 +138,10 @@ msg.constructor.ambiguous =\
The choice of Java constructor {0} matching JavaScript argument types ({1}) is ambiguous; \
candidate constructors are: {2}
# NativeJavaObject
msg.conversion.not.allowed =\
Cannot convert {0} to {1}
# NativeRegExp
msg.bad.quant =\
Invalid quantifier {0}

View File

@ -166,6 +166,10 @@ class JavaMembers {
{
Hashtable ht = isStatic ? staticMembers : members;
Object member = ht.get(name);
if (!isStatic && member == null) {
// Try to get static member from instance (LC3)
member = staticMembers.get(name);
}
if (member == null)
throw reportMemberNotFound(name);
if (member instanceof FieldAndMethods) {
@ -174,9 +178,6 @@ class JavaMembers {
Field field = null;
try {
field = (Field) member;
// XXX what was this for?
//if (obj instanceof Wrapper)
// obj = ((Wrapper)obj).unwrap();
field.set(javaObject, NativeJavaObject.coerceType(field.getType(),
value));
} catch (ClassCastException e) {

View File

@ -82,8 +82,6 @@ public class NativeJavaArray extends NativeJavaObject {
public void put(int index, Scriptable start, Object value) {
if (0 <= index && index < length) {
if (value instanceof Wrapper)
value = ((Wrapper)value).unwrap();
Array.set(array, index, NativeJavaObject.coerceType(cls, value));
return;
}

View File

@ -129,16 +129,11 @@ public class NativeJavaClass extends NativeJavaObject implements Function {
if (! (Modifier.isInterface(modifiers) ||
Modifier.isAbstract(modifiers)))
{
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Wrapper)
args[i] = ((Wrapper)args[i]).unwrap();
}
Constructor[] ctors = members.getConstructors();
Member member = NativeJavaMethod.findFunction(ctors, args);
Constructor ctor = (Constructor) member;
if (ctor == null) {
String sig = NativeJavaMethod.signature(args);
String sig = NativeJavaMethod.scriptSignature(args);
Object errArgs[] = { classObject.getName(), sig };
throw Context.reportRuntimeError(Context.getMessage(
"msg.no.java.ctor", errArgs));
@ -187,8 +182,8 @@ public class NativeJavaClass extends NativeJavaObject implements Function {
args[i] = NativeJavaObject.coerceType(paramTypes[i], args[i]);
}
try {
/* we need to force this to be wrapped, because construct _has_
* to return a scriptable */
// we need to force this to be wrapped, because construct _has_
// to return a scriptable
return
(Scriptable) NativeJavaObject.wrap(topLevel,
ctor.newInstance(args),
@ -201,7 +196,7 @@ public class NativeJavaClass extends NativeJavaObject implements Function {
("msg.cant.instantiate",
errArgs));
} catch (IllegalArgumentException argEx) {
String signature = NativeJavaMethod.signature(args);
String signature = NativeJavaMethod.scriptSignature(args);
String ctorString = ctor.toString();
Object[] errArgs = { argEx.getMessage(),ctorString,signature };
throw Context.reportRuntimeError(Context.getMessage
@ -222,11 +217,16 @@ public class NativeJavaClass extends NativeJavaObject implements Function {
/**
* Determines if prototype is a wrapped Java object and performs
* a Java "instanceof"
* a Java "instanceof".
* Exception: if value is an instance of NativeJavaClass, it isn't
* considered an instance of the Java class; this forestalls any
* name conflicts between java.lang.Class's methods and the
* static methods exposed by a JavaNativeClass.
*/
public boolean hasInstance(Scriptable value) {
if (value instanceof NativeJavaObject) {
if (value instanceof NativeJavaObject &&
!(value instanceof NativeJavaClass)) {
Object instance = ((NativeJavaObject)value).unwrap();
return getClassObject().isInstance(instance);

View File

@ -67,55 +67,58 @@ public class NativeJavaMethod extends NativeFunction implements Function {
methods = newMeths;
}
static String signature(Class type) {
if (type == null)
static String scriptSignature(Object value) {
if (value == null) {
return "null";
if (type == ScriptRuntime.BooleanClass)
return "boolean";
if (type == ScriptRuntime.ByteClass)
return "byte";
if (type == ScriptRuntime.ShortClass)
return "short";
if (type == ScriptRuntime.IntegerClass)
return "int";
if (type == ScriptRuntime.LongClass)
return "long";
if (type == ScriptRuntime.FloatClass)
return "float";
if (type == ScriptRuntime.DoubleClass)
return "double";
if (type == ScriptRuntime.StringClass)
return "string";
if (ScriptRuntime.ScriptableClass.isAssignableFrom(type)) {
if (ScriptRuntime.FunctionClass.isAssignableFrom(type))
return "function";
}
else {
Class type = value.getClass();
if (type == ScriptRuntime.UndefinedClass)
return "undefined";
return "object";
if (type == ScriptRuntime.BooleanClass)
return "boolean";
if (type == ScriptRuntime.StringClass)
return "string";
if (ScriptRuntime.NumberClass.isAssignableFrom(type))
return "number";
if (value instanceof NativeJavaObject) {
return ((NativeJavaObject)value).unwrap().getClass().getName();
}
if (value instanceof Scriptable) {
if (value instanceof Function)
return "function";
return "object";
}
return javaSignature(type);
}
if (type.isArray()) {
return signature(type.getComponentType()) + "[]";
}
return type.getName();
}
static String signature(Object[] values) {
static String scriptSignature(Object[] values) {
StringBuffer sig = new StringBuffer();
for (int i = 0; i < values.length; i++) {
if (i != 0)
sig.append(',');
sig.append(values[i] == null ? "null"
: signature(values[i].getClass()));
sig.append(scriptSignature(values[i]));
}
return sig.toString();
}
static String signature(Class[] types) {
static String javaSignature(Class type) {
if (type == null) {
return "null";
}
else if (type.isArray()) {
return javaSignature(type.getComponentType()) + "[]";
}
return type.getName();
}
static String javaSignature(Class[] types) {
StringBuffer sig = new StringBuffer();
for (int i = 0; i < types.length; i++) {
if (i != 0)
sig.append(',');
sig.append(signature(types[i]));
sig.append(javaSignature(types[i]));
}
return sig.toString();
}
@ -125,11 +128,11 @@ public class NativeJavaMethod extends NativeFunction implements Function {
if (member instanceof Method) {
paramTypes = ((Method) member).getParameterTypes();
return member.getName() + "(" + signature(paramTypes) + ")";
return member.getName() + "(" + javaSignature(paramTypes) + ")";
}
else {
paramTypes = ((Constructor) member).getParameterTypes();
return "(" + signature(paramTypes) + ")";
return "(" + javaSignature(paramTypes) + ")";
}
}
@ -142,16 +145,11 @@ public class NativeJavaMethod extends NativeFunction implements Function {
throw new RuntimeException("No methods defined for call");
}
// Eliminate useless args[0] and unwrap if required
for (int i = 0; i < args.length; i++)
if (args[i] instanceof Wrapper)
args[i] = ((Wrapper)args[i]).unwrap();
Method meth = (Method) findFunction(methods, args);
if (meth == null) {
Class c = methods[0].getDeclaringClass();
String sig = c.getName() + "." + names[0] + "(" +
signature(args) + ")";
scriptSignature(args) + ")";
Object errArgs[] = { sig };
throw Context.reportRuntimeError(
Context.getMessage("msg.java.no_such_method", errArgs));
@ -179,9 +177,28 @@ public class NativeJavaMethod extends NativeFunction implements Function {
}
}
try {
if (debug) {
printDebug("Calling", meth, args);
}
Object retval = meth.invoke(javaObject, args);
Class staticType = meth.getReturnType();
if (debug) {
Class actualType = (retval == null) ? null : retval.getClass();
System.err.println(" ----- Returned " + retval +
" actual = " + actualType +
" expect = " + staticType);
}
Object wrapped = NativeJavaObject.wrap(scope, retval, staticType);
if (debug) {
Class actualType = (wrapped == null) ? null : wrapped.getClass();
System.err.println(" ----- Wrapped as " + wrapped +
" class = " + actualType);
}
// XXX set prototype && parent
if (wrapped == Undefined.instance)
return wrapped;
@ -202,12 +219,6 @@ public class NativeJavaMethod extends NativeFunction implements Function {
}
}
/*
public Object getDefaultValue(Class hint) {
return this;
}
*/
/**
* Find the correct function to call given the set of methods
* or constructors and the arguments.
@ -339,7 +350,7 @@ public class NativeJavaMethod extends NativeFunction implements Function {
if (isCtor) {
Object errArgs[] = {
bestFit.getName(),
NativeJavaMethod.signature(args),
NativeJavaMethod.scriptSignature(args),
buf.toString()
};
errMsg =
@ -349,7 +360,7 @@ public class NativeJavaMethod extends NativeFunction implements Function {
Object errArgs[] = {
bestFit.getDeclaringClass().getName(),
bestFit.getName(),
NativeJavaMethod.signature(args),
NativeJavaMethod.scriptSignature(args),
buf.toString()
};
errMsg = Context.getMessage("msg.method.ambiguous", errArgs);
@ -417,10 +428,10 @@ public class NativeJavaMethod extends NativeFunction implements Function {
rank2 == NativeJavaObject.CONVERSION_NONTRIVIAL) {
if (toClass1.isAssignableFrom(toClass2)) {
return PREFERENCE_FIRST_ARG;
return PREFERENCE_SECOND_ARG;
}
else if (toClass2.isAssignableFrom(toClass1)) {
return PREFERENCE_SECOND_ARG;
return PREFERENCE_FIRST_ARG;
}
}
else {
@ -445,7 +456,7 @@ public class NativeJavaMethod extends NativeFunction implements Function {
System.err.println(" ----- " + msg +
member.getDeclaringClass().getName() +
"." + signature(member) +
" for arguments (" + signature(args) + ")");
" for arguments (" + scriptSignature(args) + ")");
}
}

View File

@ -219,7 +219,7 @@ public class NativeJavaObject implements Scriptable, Wrapper {
public static boolean canConvert(Object fromObj, Class to) {
int weight = NativeJavaObject.getConversionWeight(fromObj, to);
return (weight != CONVERSION_NONE);
return (weight < CONVERSION_NONE);
}
static final int JSTYPE_UNDEFINED = 0; // undefined type
@ -286,10 +286,7 @@ public class NativeJavaObject implements Scriptable, Wrapper {
if (to == Double.TYPE) {
result = 1;
}
else if (to == Boolean.TYPE) {
result = CONVERSION_NONE;
}
else {
else if (to != Boolean.TYPE) {
result = 1 + NativeJavaObject.getSizeRank(to);
}
}
@ -315,7 +312,7 @@ public class NativeJavaObject implements Scriptable, Wrapper {
else if (to == ScriptRuntime.ObjectClass) {
result = 2;
}
else if (to.isPrimitive()) {
else if (to.isPrimitive() && to != Boolean.TYPE) {
if (to == Character.TYPE) {
result = 3;
}
@ -329,7 +326,7 @@ public class NativeJavaObject implements Scriptable, Wrapper {
if (to == ScriptRuntime.ClassClass) {
result = 1;
}
if (Context.useJSObject && jsObjectClass != null &&
else if (Context.useJSObject && jsObjectClass != null &&
jsObjectClass.isAssignableFrom(to)) {
result = 2;
}
@ -346,7 +343,7 @@ public class NativeJavaObject implements Scriptable, Wrapper {
if (to == ScriptRuntime.StringClass) {
result = 2;
}
else if (to.isPrimitive()) {
else if (to.isPrimitive() && to != Boolean.TYPE) {
result =
(fromCode == JSTYPE_JAVA_ARRAY) ?
CONVERSION_NONTRIVIAL :
@ -375,7 +372,7 @@ public class NativeJavaObject implements Scriptable, Wrapper {
else if (to == ScriptRuntime.StringClass) {
result = 3;
}
else if (to.isPrimitive()) {
else if (to.isPrimitive() || to != Boolean.TYPE) {
result = 3 + NativeJavaObject.getSizeRank(to);
}
break;
@ -407,6 +404,9 @@ public class NativeJavaObject implements Scriptable, Wrapper {
else if (aType == Byte.TYPE) {
return 7;
}
else if (aType == Boolean.TYPE) {
return CONVERSION_NONE;
}
else {
return 8;
}
@ -426,6 +426,9 @@ public class NativeJavaObject implements Scriptable, Wrapper {
else if (value instanceof NativeJavaArray) {
return JSTYPE_JAVA_ARRAY;
}
else if (value instanceof NativeString) {
return JSTYPE_STRING;
}
else if (value instanceof NativeJavaObject) {
return JSTYPE_JAVA_OBJECT;
}
@ -459,14 +462,41 @@ public class NativeJavaObject implements Scriptable, Wrapper {
/**
* Type-munging for field setting and method invocation.
* Conforms to LC3 specification
*/
public static Object coerceType(Class type, Object value) {
// Don't coerce null to a string (or other object)
if (value == null) {
// raise error if type.isPrimitive()
if (type.isPrimitive()) {
reportConversionError(value, type);
}
return null;
}
else if (value == Undefined.instance) {
if (type == ScriptRuntime.StringClass ||
type == ScriptRuntime.ObjectClass) {
return "undefined";
}
else {
// report conversion error
reportConversionError("undefined", type);
}
}
// For ScriptRuntime.finalClasses we can compare valueClass to ScriptRuntime.aClass object
// Special case: converting JS numbers to Objects
// Done before we unwrap, to distinguish from a
// wrapped java.lang.Number.
if (type == ScriptRuntime.ObjectClass && value instanceof Number) {
return new Double(((Number)value).doubleValue());
}
// Unwrap at this point; callers do not need to unwrap
if (value instanceof Wrapper) {
value = ((Wrapper)value).unwrap();
}
// For final classes we can compare valueClass to a Class object
// rather than using instanceof
Class valueClass = value.getClass();
@ -479,29 +509,43 @@ public class NativeJavaObject implements Scriptable, Wrapper {
return ScriptRuntime.toString(value);
// Boolean
if (type == Boolean.TYPE || type == ScriptRuntime.BooleanClass)
return new Boolean(ScriptRuntime.toBoolean(value));
if (type == Boolean.TYPE || type == ScriptRuntime.BooleanClass) {
// Under LC3, only JS Booleans can be coerced into a Boolean value
if (valueClass == ScriptRuntime.BooleanClass) {
return value;
}
else {
reportConversionError(value, type);
}
}
// Character
if (type == Character.TYPE || type == ScriptRuntime.CharacterClass) {
// Special case for converting a single char string to a character
if (valueClass == ScriptRuntime.StringClass && ((String) value).length() == 1)
return new Character(((String) value).charAt(0));
return new Character((char)ScriptRuntime.toInteger(value));
/*
if (valueClass == ScriptRuntime.StringClass) {
String string = (String)value;
if (string.length() == 1) {
char ch = string.charAt(0);
// XXX: Next test not in LC3 spec, but is backwardly
// compatible and satisfies regression tests
if (!Character.isDigit(ch)) {
return new Character(ch);
}
}
}
*/
if (valueClass == ScriptRuntime.CharacterClass) {
return value;
}
return new Character((char)toInteger(value,
ScriptRuntime.CharacterClass,
Character.MIN_VALUE,
Character.MAX_VALUE));
}
// Integer, Long, Char, Byte
if (type == ScriptRuntime.IntegerClass || type == Integer.TYPE)
return new Integer((int)ScriptRuntime.toInteger(value));
if (type == ScriptRuntime.LongClass || type == Long.TYPE)
return new Long((long)ScriptRuntime.toInteger(value));
if (type == ScriptRuntime.ByteClass || type == Byte.TYPE)
return new Byte((byte)ScriptRuntime.toInteger(value));
if (type == ScriptRuntime.ShortClass || type == Short.TYPE)
return new Short((short)ScriptRuntime.toInteger(value));
// Double, Float
if (type == ScriptRuntime.DoubleClass || type == Double.TYPE) {
return valueClass == ScriptRuntime.DoubleClass
@ -510,14 +554,81 @@ public class NativeJavaObject implements Scriptable, Wrapper {
}
if (type == ScriptRuntime.FloatClass || type == Float.TYPE) {
return valueClass == ScriptRuntime.FloatClass
? value
: new Float(ScriptRuntime.toNumber(value));
if (valueClass == ScriptRuntime.FloatClass) {
return value;
}
else {
double number = ScriptRuntime.toNumber(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));
}
}
if (valueClass == ScriptRuntime.DoubleClass)
return value;
// If JSObject compatibility is enabled, and the method wants it,
// wrap the Scriptable value in a JSObject.
if (Context.useJSObject && jsObjectClass != null &&
@ -540,12 +651,50 @@ public class NativeJavaObject implements Scriptable, Wrapper {
}
}
if (ScriptRuntime.NumberClass.isInstance(value))
return new Double(((Number) value).doubleValue());
return value;
}
static long toInteger(Object value, Class type, long min, long max) {
double d;
if (value instanceof Number) {
d = ((Number)value).doubleValue();
}
else if (value instanceof String) {
d = ScriptRuntime.toNumber((String)value);
}
else if (value instanceof Scriptable) {
d = ScriptRuntime.toNumber(value);
}
else {
// XXX: is this correct?
d = ScriptRuntime.toNumber(value.toString());
}
if (Double.isInfinite(d) || Double.isNaN(d)) {
// Convert to string first, for more readable message
reportConversionError(value.toString(), 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(value.toString(), type);
}
return (long)d;
}
static void reportConversionError(Object value, Class type) {
Object[] args = {value, type};
throw Context.reportRuntimeError(Context.getMessage("msg.conversion.not.allowed", args));
}
public static void initJSObject() {
if (!Context.useJSObject)
return;

View File

@ -138,6 +138,10 @@ msg.constructor.ambiguous =\
The choice of Java constructor {0} matching JavaScript argument types ({1}) is ambiguous; \
candidate constructors are: {2}
# NativeJavaObject
msg.conversion.not.allowed =\
Cannot convert {0} to {1}
# NativeRegExp
msg.bad.quant =\
Invalid quantifier {0}