The Mozilla
Organization
Our Mission
Who We Are
Getting Involved
Community
Editorials
What's New
Development
Roadmap
Module Owners
Blue Sky
Projects
Status
Tools
Products
Source Code
Binaries
Documentation
License Terms
Bug Reports
Search
Feedback


1 Motivation

The technique that earlier versions of LiveConnect use to invoke overloaded Java methods from JavaScript is dirt-simple: The first applicable method that is enumerated by the Java VM is chosen.  (Here, "applicable" means that the method name and the number of arguments match and that each of the JavaScript arguments can be converted to the corresponding Java type listed in the method's signature.)

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).

2 Introduction

Ideally, invocation of Java methods from JavaScript would make use of Java's own rules for overloaded method resolution, but using JS's runtime types rather than Java's compile-time types.  However, that's not feasible for several reasons:

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 {
   static public int numericArg(int x)   { return 1; }
   static public int numericArg(byte x)  { return 2; }
   static public int numericArg(float x) { return 3; }
}
Which method should be invoked when integralArg is invoked from JavaScript ?
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.

3 Invocation of Java Methods from JavaScript

Unfortunately, there is no way to both completely preserve backward compatibility and cure LiveConnect of its method invocation ills.  The new approach is to apply heuristics to guess the intended method given the runtime JavaScript argument types and the type signatures of the candidate Java methods.  Informally, the method with Java parameter types that most closely match the JavaScript types is chosen.  For example, when converting from a JavaScript number type, a method that specifies a double argument is preferred to one that requires a java.lang.String.

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.

3.1 Method Accessibility and Applicability

The first step in resolving a method invocation is to determine which methods of a class are accessible and applicable.  A Java method is accessible and applicable if all of the following are true:
  • The method is public.
  • If the invocation is a static invocation, the method must be a static method.  If the invocation is an instance invocation, the method must not be static.
  • The number of parameters in the method declaration equals the number of argument expressions in the method invocation.
  • The type of each actual argument can be converted by LiveConnect method invocation conversion (See Section 3.3).
If there are no applicable methods for an invocation, a NoSuchJavaMethod exception is thrown.  If there is only one applicable method, it is the one invoked.

3.2 Choose the Preferred Method

When choosing between two or more applicable methods, an algorithm is used that is similar in spirit to the ones used in Java and C++:
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
    for all j from 1 to n.
A method is said to be maximally preferred for a method invocation if it is applicable and there is no more preferred applicable method.  If there is only one maximally preferred method, that method is necessarily preferred to all other applicable methods and it is the one invoked.  If there is more than one maximally preferred method, an AmbiguousJavaMethod JavaScript exception is thrown.

3.3 Allowed Method Argument Conversions

The following sections detail the allowed conversions of JavaScript values to Java values when converting arguments for method invocation.  These rules remain essentially unchanged from earlier LiveConnect implementations.

3.3.1 undefined

 
Java argument type
Conversion Technique
java.lang.Object 
java.lang.String
"undefined"1
boolean
false (Should this result in a runtime error instead ?)
double 
float
NaN (Should this result in a runtime error instead ?)
long 
int 
short 
char 
byte
0 (zero) (Should this result in a runtime error instead ?)
1The java.lang.String resulting from the conversion should be interned, so that it can be compared to other string values using the == operator.  There 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.

3.3.2 Boolean

 
Java argument type
Conversion Technique
boolean
Map true/false directly to Java equivalent
java.lang.Boolean 
java.lang.Object
Construct new instance of java.lang.Boolean.2
java.lang.String
true  ==> "true" 3
false ==> "false"
double4
float4
long 
int 
short 
char 
byte
true  ==> 1 
false ==> 0
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.

3The java.lang.String resulting from the conversion should be interned, so that it can be compared to other string values using the == operator.

4The conversion to Java float and double from JS boolean is probably not useful, but it's left in for backward compatibility with LC1
 
 

3.3.3 Number

Java argument type
Conversion Technique
double 
Transfer exact value to Java with 
no rounding or loss of magnitude/sign.
java.lang.Double 
java.lang.Object
Create new instance of java.lang.Double, transferring exact value to Java with no rounding or loss of magnitude/sign.
float
  • Round JS number to float precision.
  • Unrepresentably large values are converted to +/- infinity.
long 
int 
short 
byte 
char
  • Round JS number to integral value using round-to-negative-infinity mode.
  • Numbers with a magnitude too large to be represented in the target integral type result in a runtime error.
  • NaNs are converted to zero. (Should NaN's result in a runtime error instead ?)
java.lang.String
true  ==> "true"3
false ==> "false"
boolean
0, NaN ==> false 
all other values ==> true

3.3.4 Strings

Java argument type
Conversion Technique
java.lang.String 
java.lang.Object
Convert from Unicode JS 
string to Unicode java.lang.String5
double 
float 
long 
int 
short 
byte
  1. Convert string to number per ECMA 9.3.1
  2. Convert Result(1) to Java numeric type using rules in Section 3.3.3.
char
  • For one-character strings, result is Unicode character.5
  • Otherwise, convert to number, using above rule.
  • boolean
    empty string ==> false 
    all other values ==> true
    5Conversion added in LiveConnect version 2.

    3.3.5 Null

    Java argument type
    Conversion Technique
    Any class or interface type
    null
    double 
    float 
    long 
    int 
    short 
    byte 
    char
    0 (zero)
    boolean
    false

    3.3.6 Object

    3.3.6.1 JavaObject/JavaArray

     
    Java argument type
    Conversion Technique
    Any interface or class that is assignment-compatible with the Java object obtained by unwrapping the JS object, i.e. the unwrapped JavaObject is an instanceof() the Java argument type.
    Unwrap JS object to obtain Java object
    java.lang.String
    Call the unwrapped object's toString() method and return the result as a new java.lang.String.

    3.3.6.2 JavaClass

     
    Java argument type
    Conversion Technique
    java.lang.Class5
    Extract corresponding Java class object
    java.lang.JSObject 
    java.lang.Object
    Wrap JS object in new instance of java.lang.JSObject
    java.lang.String
    Call the JavaClass toString() method and return the result as a java.lang.String.

    3.3.6.3 Other JavaScript Objects

     
    Java argument type
    Conversion Technique
    java.lang.JSObject 
    java.lang.Object
    Wrap JS object in new instance of java.lang.JSObject
    java.lang.String
    Call the JS object's toString() method and return the result as a java.lang.String.

    3.4 Preferred Argument Conversions

    When converting from JavaScript to Java types, certain conversions are more "natural" and, hence, are preferred.

    3.4.1 undefined

    There is no preference among Java types for converting from the JavaScript undefined value.

    3.4.2 Boolean

    Java argument type,
    in decreasing order of preference
    boolean
    java.lang.Boolean
    java.lang.Object 
    java.lang.String
    long, int, short, char, byte
    double, float

    3.4.3 Number

    Java argument type,
    in decreasing order of preference
    double 
    java.lang.Double
    float
    long
    int
    short
    char
    byte
    java.lang.String
    boolean
    java.lang.Object

    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.

    3.4.4 Strings

    Java argument type,
    in decreasing order of preference
    java.lang.String
    java.lang.Object
    char
    double, float, long, int, short, byte
    boolean

    3.4.5 Null

    Java argument type,
    in decreasing order of preference
    Any class or interface type
    double, float, long, int, short, byte, char, boolean

    3.4.6 Object

    3.4.6.1 JavaObject/JavaArray

    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
    • An instance of T is assignable to a variable of type S, i.e. T instanceof S is true
    • An instance of S is assignable to a variable of type U, i.e. S instanceof U is true
    • S and U are not the same types

    3.4.6.2 JavaClass

    Java argument type,
    in decreasing order of preference
    java.lang.Class
    java.lang.JSObject
    java.lang.Object
    java.lang.String

    3.4.6.3 Other JavaScript Objects

    Java argument type,
    in decreasing order of preference
    java.lang.JSObject
    java.lang.Object
    java.lang.String

    3.5 Explicit Method Specification

    LC3 allows explicitly specifying an overloaded method and bypassing the resolution process. Explicit method specification is typically used when an Java method is overloaded using Java numeric types:

    class Ambiguous {
       static public int numericArg(int x)   { return 1; }
       static public int numericArg(byte x)  { return 2; }
       static public int numericArg(float x) { return 3; }
    }

    In this case it is possible to specify that numericArg(int) should be called using the following syntax:

    intNumericArg = Packages.Ambiguous["numericArg(int)"];
    intNumericArg(5); // returns 1

    By using named property access and passing the name of the method with type information, an object will be returned that can be used to call the desired method.

    The same effect can be achieved with this more compact syntax:

    Packages.Ambiguous["numericArg(int)"](5); // returns 1

     



    Copyright © 1998 The Mozilla Organization.