From 5f4bba2ae5b9012d3cb4bcbf9c53e8b3a0f916dd Mon Sep 17 00:00:00 2001 From: "frankm%eng.sun.com" Date: Fri, 11 Jun 1999 01:24:40 +0000 Subject: [PATCH] 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. git-svn-id: svn://10.0.0.236/trunk@34731 18797224-902f-48f8-a5cc-f745e15eee43 --- .../org/mozilla/javascript/JavaMembers.java | 7 +- .../mozilla/javascript/NativeJavaArray.java | 2 - .../mozilla/javascript/NativeJavaClass.java | 22 +- .../mozilla/javascript/NativeJavaMethod.java | 111 +++++---- .../mozilla/javascript/NativeJavaObject.java | 221 +++++++++++++++--- .../javascript/resources/Messages.properties | 4 + .../org/mozilla/javascript/JavaMembers.java | 7 +- .../mozilla/javascript/NativeJavaArray.java | 2 - .../mozilla/javascript/NativeJavaClass.java | 22 +- .../mozilla/javascript/NativeJavaMethod.java | 111 +++++---- .../mozilla/javascript/NativeJavaObject.java | 221 +++++++++++++++--- .../javascript/resources/Messages.properties | 4 + 12 files changed, 530 insertions(+), 204 deletions(-) diff --git a/mozilla/js/rhino/org/mozilla/javascript/JavaMembers.java b/mozilla/js/rhino/org/mozilla/javascript/JavaMembers.java index d983b6c25f9..ba6d43fb4cd 100644 --- a/mozilla/js/rhino/org/mozilla/javascript/JavaMembers.java +++ b/mozilla/js/rhino/org/mozilla/javascript/JavaMembers.java @@ -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) { diff --git a/mozilla/js/rhino/org/mozilla/javascript/NativeJavaArray.java b/mozilla/js/rhino/org/mozilla/javascript/NativeJavaArray.java index 8e6f3d1c8de..2a1b705d8f5 100644 --- a/mozilla/js/rhino/org/mozilla/javascript/NativeJavaArray.java +++ b/mozilla/js/rhino/org/mozilla/javascript/NativeJavaArray.java @@ -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; } diff --git a/mozilla/js/rhino/org/mozilla/javascript/NativeJavaClass.java b/mozilla/js/rhino/org/mozilla/javascript/NativeJavaClass.java index 7e4506c84ad..42f350fd96b 100644 --- a/mozilla/js/rhino/org/mozilla/javascript/NativeJavaClass.java +++ b/mozilla/js/rhino/org/mozilla/javascript/NativeJavaClass.java @@ -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); diff --git a/mozilla/js/rhino/org/mozilla/javascript/NativeJavaMethod.java b/mozilla/js/rhino/org/mozilla/javascript/NativeJavaMethod.java index bfabc918ab7..13715690b7e 100644 --- a/mozilla/js/rhino/org/mozilla/javascript/NativeJavaMethod.java +++ b/mozilla/js/rhino/org/mozilla/javascript/NativeJavaMethod.java @@ -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) + ")"); } } diff --git a/mozilla/js/rhino/org/mozilla/javascript/NativeJavaObject.java b/mozilla/js/rhino/org/mozilla/javascript/NativeJavaObject.java index fd95b3496ec..b79a50960ac 100644 --- a/mozilla/js/rhino/org/mozilla/javascript/NativeJavaObject.java +++ b/mozilla/js/rhino/org/mozilla/javascript/NativeJavaObject.java @@ -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; diff --git a/mozilla/js/rhino/org/mozilla/javascript/resources/Messages.properties b/mozilla/js/rhino/org/mozilla/javascript/resources/Messages.properties index a0cd51cdcc0..32fa6638e60 100644 --- a/mozilla/js/rhino/org/mozilla/javascript/resources/Messages.properties +++ b/mozilla/js/rhino/org/mozilla/javascript/resources/Messages.properties @@ -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} diff --git a/mozilla/js/rhino/src/org/mozilla/javascript/JavaMembers.java b/mozilla/js/rhino/src/org/mozilla/javascript/JavaMembers.java index d983b6c25f9..ba6d43fb4cd 100644 --- a/mozilla/js/rhino/src/org/mozilla/javascript/JavaMembers.java +++ b/mozilla/js/rhino/src/org/mozilla/javascript/JavaMembers.java @@ -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) { diff --git a/mozilla/js/rhino/src/org/mozilla/javascript/NativeJavaArray.java b/mozilla/js/rhino/src/org/mozilla/javascript/NativeJavaArray.java index 8e6f3d1c8de..2a1b705d8f5 100644 --- a/mozilla/js/rhino/src/org/mozilla/javascript/NativeJavaArray.java +++ b/mozilla/js/rhino/src/org/mozilla/javascript/NativeJavaArray.java @@ -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; } diff --git a/mozilla/js/rhino/src/org/mozilla/javascript/NativeJavaClass.java b/mozilla/js/rhino/src/org/mozilla/javascript/NativeJavaClass.java index 7e4506c84ad..42f350fd96b 100644 --- a/mozilla/js/rhino/src/org/mozilla/javascript/NativeJavaClass.java +++ b/mozilla/js/rhino/src/org/mozilla/javascript/NativeJavaClass.java @@ -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); diff --git a/mozilla/js/rhino/src/org/mozilla/javascript/NativeJavaMethod.java b/mozilla/js/rhino/src/org/mozilla/javascript/NativeJavaMethod.java index bfabc918ab7..13715690b7e 100644 --- a/mozilla/js/rhino/src/org/mozilla/javascript/NativeJavaMethod.java +++ b/mozilla/js/rhino/src/org/mozilla/javascript/NativeJavaMethod.java @@ -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) + ")"); } } diff --git a/mozilla/js/rhino/src/org/mozilla/javascript/NativeJavaObject.java b/mozilla/js/rhino/src/org/mozilla/javascript/NativeJavaObject.java index fd95b3496ec..b79a50960ac 100644 --- a/mozilla/js/rhino/src/org/mozilla/javascript/NativeJavaObject.java +++ b/mozilla/js/rhino/src/org/mozilla/javascript/NativeJavaObject.java @@ -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; diff --git a/mozilla/js/rhino/src/org/mozilla/javascript/resources/Messages.properties b/mozilla/js/rhino/src/org/mozilla/javascript/resources/Messages.properties index a0cd51cdcc0..32fa6638e60 100644 --- a/mozilla/js/rhino/src/org/mozilla/javascript/resources/Messages.properties +++ b/mozilla/js/rhino/src/org/mozilla/javascript/resources/Messages.properties @@ -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}