Closed Bug 223435 Opened 21 years ago Closed 21 years ago

Automatic conversion of JS functions into Java interfaces

Categories

(Rhino Graveyard :: Core, enhancement)

enhancement
Not set
normal

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: igor, Assigned: igor)

Details

Attachments

(3 files, 2 obsolete files)

It would be nice if Rhino would automatically create JavaAdapter from JavaScript
object when calling Java methods if corresponding Java parameter is interface.

For example, currently to add an action listener to Swing button
SwingApplication.js example uses:

    button.addActionListener(new ActionListener({
	actionPerformed : function() {
	    numClicks += 1;
	    label.setText(labelPrefix + numClicks);
	}
    }));

But it would be much simpler to write just

    button.addActionListener({
	actionPerformed : function() {
	    numClicks += 1;
	    label.setText(labelPrefix + numClicks);
	}
    });

which would create the adapter wrapper automatically and similarly it would be
possible to replace

frame.addWindowListener(new WindowAdapter({
    windowClosing : function() {
	java.lang.System.exit(0);
    }
}) );

by

frame.addWindowListener({
    windowClosing : function() {
	java.lang.System.exit(0);
    }
});
 
Yet another usability extension would be to allow to pass JS function where
corresponding Java type is an interface with a single method. With this
extension the first example becomes simply:

    button.addActionListener(function() {
    	numClicks += 1;
	label.setText(labelPrefix + numClicks);
    });

Moreover, with such extension one would get automatic proper binding for DOM
event listeners. Java bindings for DOM states that event listener should
implement EventHandler interface which contains single handleEvent method while
DOM bindings for ECMAScript accepts arbitrary function.
Attached patch Implementing first proposal (obsolete) — Splinter Review
Patch implements the first proposal: it automatically creates adapter objects
when calling Java methods and when corresponding argument type is interface.
Automatic creation of wrapper objects is dangerous feature since the wrapper
object is not available to JS so if one would register a listener through:

var listener = {
    windowClosing : function() {
	java.lang.System.exit(0);
    }
};

frame.addWindowListener(listener);

then 
 
frame.removeWindowListener(listener);

would not work since a new wrapper for the listener object would be created.

The proper implementation should somehow store the wrapper so it would be
reused and frame.removeWindowListener(listener) would work. Under JDK 1.2
WeakHash can be used to store the mapping but it would make dependence on JDK
1.2 too heavy when MS JVM still should be accounted for. (In fact, I know one
case when Rhino is used with JDK 1.1.8 for Solaris since later JDK needs to
much memory...) 

For single-method interface to function map such implementation is possible
under restriction that only BaseFunction instances would be allowed in such
usage so BaseFunction can store the wrapping. Since all usable cases for the
automatic conversion involves BaseFunction, this is not a serious restriction. 

For this reasons the new patch does not have any support for automatic wrapping
of generic JS object but just implements Java interface to JS function mapping.
The patch does not create the standard adapter to take advantage of the fact
that the glue object passed to Java method is not visible to JS. It allows not
to create NativeJavaObject for the glue itself that the standard Java adapter
would do. Since the glue need to support only interfaces, the patch uses
special IFGlue class as a base for all generated code to minimize amount of
generated code.

In addition, patch adds optimization to the current adapter code to remove
calling Context.enter/Context.exit when converting Java to JS arguments for
each non-primitive type since the conversion can be done when Context instance
is obtained for calling the function itself.
Attachment #133959 - Attachment is obsolete: true
To allow to write:

var t = java.lang.Thread(function f() { .... } )

to mean to create a thread that will call f() in its run method and not create
Thread named by f.toString() the patch update makes conversion
function->interface to weight 1 if interface contains single method.
Attachment #134210 - Attachment is obsolete: true
Changing the title:

Automatic conversion of JS object into JavaAdapter extending/implementing
arbitrary Java class would be sugar that is very easy to misuse. My initial
intention was to have something that allow to simplify JS implementation of
event listening code in JS and the committed part took care about common case of
interfaces with single method. But in the rest of useful cases event listening
interfaces would have multiple methods with exactly the same signature like
WindowListener, MouseListener etc. tand it is sufficient to provide sugar only
for this case.

Thus the idea would be allow to pass JS function to Java method not only when
corresponding Java type is an interface with single method but also for
interfaces with several methods with exactly the same signature. To distinguish
between methods in JS the glue code would append method name as the last
parameter. Since the signature for all methods are the same, the position of
this last parameter would be the same as well and one could write:

frame.addWindowListener(function(event, methodName) {
    if (methodName == "windowClosing") {
        java.lang.System.exit(0);
    }
});
Summary: Automatic creation of JavaAdapter instances → Automatic conversion of JS functions into Java interfaces
The patch changes the current glue code to pass the name of the called
interface method as the last parameter to JS function.

This is useful for debugging, for example, even without extending the
conversion to interfaces with multiple methods with the same signature.
The patch checks if all additional interface methods have the same signature,
then the conversion is allowed and the initial JS function will be called for
each method.
I committed the last patches which allows to mark bug as fixed
Status: NEW → RESOLVED
Closed: 21 years ago
Resolution: --- → FIXED
Target Milestone: --- → 1.5R5
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: