Last modified:
The Netscape JVM always enumerated methods using the order in which they appeared in their classfile, so rearrangement of methods in the Java source files was often required to invoke the desired method. This behavior was sometimes painful to developers, both because source was not always available and because the static nature of this method resolution algorithm sometimes made it impossible to choose a different method resolution at each invocation site. Most importantly, the willingness of LiveConnect to convert method arguments from JavaScript types to wildly different Java types sometimes lead to unexpected method resolutions. A more serious problem has cropped up recently with Netscape's migration to 3rd-party JVM's: the enumeration order of methods is not defined by the Java specification, so overloaded method resolution can occur differently depending on which vendor's JVM is being used in conjunction with LiveConnect. It is these latter two difficulties which are addressed by this proposal for LiveConnect version 3 (LC3).
First, Java method signatures distinguish between a variety of numeric types, i.e. byte, char, short, int, long, float, double. JavaScript collapses all numeric types, whether integral or floating point, into a single number type. Given this Java class declaration:
class Ambiguous {Which method should be invoked when integralArg is invoked from JavaScript ?
static public int numericArg(int x) { return 1; }
static public int numericArg(byte x) { return 2; }
static public int numericArg(float x) { return 3; }
}
Packages.Ambiguous.numericArg(3);Finally, there is precedent set by previous versions of LiveConnect which are all too willing to convert JavaScript arguments to unrelated Java types, e.g. the conversion of a JavaScript boolean value to a string or an instance of java.lang.Boolean.
Although the the choice of method to be invoked may be different in LC3 compared to earlier versions of LiveConnect, the permitted conversions of JavaScript arguments to Java types has not been changed. Hence, backward compatibility is preserved for invocations of non-overloaded methods or in cases where only a single method is compatible with the argument types used.
Suppose that U and S are both applicable methods for an invocation, each having n parameters. Suppose, moreover, that the Java types of the parameters for method U are u1,...,un and the Java types of the parameters for method S are s1,...,sn. Finally, the runtime JavaScript types of the actual arguments are t1,...,tn. Then the method U is preferred over method S iff
- uj and sj are the same type, or
- conversion to type uj is preferred to the conversion to type sj when converting from tj
|
|
java.lang.String |
"undefined"1 |
1There is some ambiguity to the result because the JS string literal "undefined" and the undefined JS value are both converted to the same Java string, but this wart is necessary to maintain backward compatibility with LC1. Really, it would be best if conversion from JS undefined to all Java types caused an error.
|
|
|
Map true/false directly to Java equivalent |
java.lang.Object |
Construct new instance of java.lang.Boolean.2 |
|
true ==> "true" |
2Each argument conversion must result in a new java.lang.Boolean instance. For example, it is not permitted to always use java.lang.Boolean.TRUE and java.lang.Boolean.FALSE.
|
|
|
Transfer exact value to Java with |
java.lang.Object |
Create new instance of java.lang.Double, transferring exact value to Java with no rounding or loss of magnitude/sign. |
|
|
int short byte char |
|
|
Convert number to string per ECMA 9.8.1, ToString() applied to Number type |
3In pre-LC3 versions of LiveConnect, conversion from either NaN's or numbers with a magnitude too large to be represented in the target integral type was ill-defined and had platform-dependent behavior.
|
|
java.lang.Object |
Convert from Unicode JS |
float5 long5 int5 short5 byte5 |
|
|
|
5Conversion added in LiveConnect version 2.
|
|
|
null |
|
|
|
Unwrap JS object to obtain Java object |
|
Call the unwrapped object's toString() method and return the result as a new java.lang.String. |
|
|
|
|
|
Unwrap JS object to obtain Java object |
|
Call the unwrapped array's toString() method and return the result as a new java.lang.String. |
|
|
|
Extract corresponding Java class object |
java.lang.Object |
Wrap JS object in new instance of java.lang.JSObject |
|
Call the JavaClass toString() method and return the result as a java.lang.String. |
|
|
java.lang.Object |
Wrap JS object in new instance of java.lang.JSObject |
|
Call the JS object's toString() method and return the result as a java.lang.String. |
|
|
|
|
java.lang.Object |
Wrap JS object in new instance of java.lang.JSObject |
|
Call the JS object's toString() method and return the result as a java.lang.String. |
|
|
in decreasing order of preference |
|
|
|
|
in decreasing order of preference |
|
|
|
|
|
|
|
|
|
|
Rationale: The preference for floating-point types over integral types is likely to be the largest culprit in exposing incompatibilities with earlier versions of LiveConnect. However, double is the only primitive Java type guaranteed not to overflow or lose precision when converting from a JS number, so it should be preferred to the other Java numeric types.
in decreasing order of preference |
|
|
|
|
in decreasing order of preference |
|
|
|
|
|
|
|
|
|
6Among Java reference types, further processing is required. Intuitively, the rule for preference among Java types when converting from a Java object that is wrapped in a JS object is that the most specific class or interface is preferred. More formally, let T be the Java class of an unwrapped JavaObject. Let S and U be class or interface types. S is preferred to U iff
in decreasing order of preference |
|
|
in decreasing order of preference |
|
|
|
|
in decreasing order of preference |
Any Java array type |
|
|
|
in decreasing order of preference |
|
|
|
|
|
|
|
|
|
|
class Ambiguous {In this case it is possible to specify that numericArg(int,byte) should be called using the following syntax:
static public int numericArg(int x, byte y) { return 1; }
static public int numericArg(byte x, char y) { return 2; }
static public int numericArg(float x, int y) { return 3; }
}
intNumericArg = Packages.Ambiguous["numericArg(int,byte)"];By using named property access and passing the name of the method with type signature information, an object will be returned that can be used to call the desired method. Explicit method specification can be used on both instance and static methods.
intNumericArg(5); // returns 1
The same effect can be achieved without using a temporary value to hold the method:
Packages.Ambiguous["numericArg(int,byte)"](5); // returns 1A similar syntax can be used to explicitly specify selection of an overloaded constructor. For example, the following code invokes the java.lang.String constructor that accepts one argument, a one-dimensional array of characters:
new java.lang.String["(char[])"](c);