Tuesday, August 5th, 2008

JavaScript Overlay Types in GWT

Category: GWT

Bruce Johnson of the GWT team has continued the deep dive into GWT with a posting on a new GWT 1.5 feature: JavaScript overlay types. This feature goes beyond the JNSI technique to “make it easy to integrate entire families of JavaScript objects into your GWT project. There are many benefits of this technique, including the ability to use your Java IDE’s code completion and refactoring capabilities even as you’re working with untyped JavaScript objects.”

The first example that Bruce gives is to mix JSON objects with Java:

javascript

  1. var jsonData = [
  2.   { "FirstName" : "Jimmy", "LastName" : "Webber" },
  3.   { "FirstName" : "Alan",  "LastName" : "Dayal" },
  4.   { "FirstName" : "Keanu", "LastName" : "Spoon" },
  5.   { "FirstName" : "Emily", "LastName" : "Rudnick" }
  6. ];
  1. // An overlay type
  2. class Customer extends JavaScriptObject {
  3.  
  4.   // Overlay types always have protected, zero-arg ctors
  5.   protected Customer() { }
  6.    
  7.   // Typically, methods on overlay types are JSNI
  8.   public final native String getFirstName() /*-{ return this.FirstName; }-*/;
  9.   public final native String getLastName()  /*-{ return this.LastName;  }-*/;
  10.    
  11.   // Note, though, that methods aren't required to be JSNI
  12.   public final String getFullName() {
  13.     return getFirstName() + " " + getLastName();
  14.   }
  15. }
  1. // the glue
  2.  
  3. class MyModuleEntryPoint implements EntryPoint {
  4.   public void onModuleLoad() {
  5.     Customer c = getFirstCustomer();
  6.     // Yay! Now I have a JS object that appears to be a Customer
  7.     Window.alert("Hello, " + c.getFirstName());
  8.   }
  9.  
  10.   // Use JSNI to grab the JSON object we care about
  11.   // The JSON object gets its Java type implicitly based on the method's return type
  12.   private native Customer getFirstCustomer() {
  13.     // Get a reference to the first customer in the JSON array from earlier
  14.     return $wnd.jsonData[0];
  15.   }
  16. }

Bruce then shows us some performance wins that you get, as GWT gets to do a lot of inlining:

A quick digression for compiler geeks. Another neat thing about overlay types is that you can augment the Java type without disturbing the underlying JavaScript object. In the example above, notice that we added the getFullName() method. It’s purely Java code — it doesn’t exist on the underlying JavaScript object — and yet the method is written in terms of the underlying JavaScript object. In other words, the Java view of the JavaScript object can be richer in functionality than the JavaScript view of the same object but without having to modify the underlying JS object, neither the instance nor its prototype.

(This is still part of the digression.) This cool wackiness of adding new methods to overlay types is possible because the rules for overlay types by design disallow polymorphic calls; all methods must be final and/or private. Consequently, every method on an overlay type is statically resolvable by the compiler, so there is never a need for dynamic dispatch at runtime. That’s why we don’t have to muck about with an object’s function pointers; the compiler can generate a direct call to the method as if it were a global function, external to the object itself. It’s easy to see that a direct function call is faster than an indirect one. Better still, since calls to methods on overlay types can be statically resolved, they are all candidates for automatic inlining, which is a Very Good Thing when you’re fighting for performance in a scripting language.

From this Java code:

  1. class MyModuleEntryPoint implements EntryPoint {
  2.   public void onModuleLoad() {
  3.     JsArray<customer> cs = getCustomers();
  4.     for (int i = 0, n = cs.length(); i < n; ++i) {
  5.       Window.alert("Hello, " + cs.get(i).getFullName());
  6.     }
  7.   }
  8.  
  9.   // Return the whole JSON array, as is
  10.   private final native JsArray<Customer> getCustomers() /*-{
  11.     return $wnd.jsonData;
  12.   }-*/;
  13. }

The compiler inlines away to get to the followinig (not obfuscated to see):

function $onModuleLoad(){
var cs, i, n;
cs = $wnd.jsonData;
for (i = 0, n = cs.length; i < n; ++i) { $wnd.alert('Hello, ' + (cs[i].FirstName + ' ' + cs[i].LastName)); } } [/javascript]

Posted by Dion Almaer at 8:10 am
Comment here

+++--
3.5 rating from 24 votes

Comments Here »

Comments feed TrackBack URI

Leave a comment

You must be logged in to post a comment.