Last week, Google announced that it had fully open-sourced its Google Web Toolkit, or GWT, a set of tools that enables Java developers to code up interactive, browser-based applications in Java, and then have GWT automagically convert that Java application into browser-specific JavaScript.
At the heart of GWT is a Java-to-JavaScript compiler that performs extensive static analysis on Java source files, and then outputs highly optimized JavaScript translations of the Java source code. The compiler output is tailored to satisfy the idiosyncrasies of the four most popular Web browsers in use today. Artima spoke with Scott Blum, the Google software engineer behind GWT's Java-to-JavaScript compiler.
Frank Sommers: Could you start by giving us an overview of the GWT development process?
Scott Blum: The basic idea is that you write your application in Java, debug it in Java, and then our compiler takes your Java program and turns it into JavaScript.
In order to allow us to do a lot of optimization, the basic strategy the compiler takes is monolithic compilation. That's our mantra. We figure out the exact and complete set of classes in your application, and where the entry point to your application is, and we use that constraint system to allow us to do a lot of really neat optimizations.
For example, suppose you have an application that is written in terms of the List
interface, because you are a good object-oriented developer and use abstract types. But let's say that you only ever instantiate the ArrayList
as a concrete implementation of List
. Everywhere we make a call to a List
method, we figure out at compile time that that's actually a call to an ArrayList
method, and in cases where it's appropriate, we actually inline that call.
Probably the biggest advantage that brings to the table is when we compile for different platforms: we bring in only classes that are specific to that platform. For example, when we're compiling for Firefox, we only bring the DOM implementation for Firefox into the compile. That cuts down the code size, which is a very important thing for us.
Frank Sommers: What are the biggest differences in the JavaScript the GWT generates for the various browsers?
Scott Blum: It turns out that at the language level most of the browsers are pretty uniform. The actual JavaScript programming model itself is implemented fairly consistently. We don't really have a lot in the way of specific idioms we use on particular browsers at the compilation level. In fact, the compiler doesn't have any special knowledge of the browser that's being targeted.
The main differences come in when you start dealing with the DOM on different browsers, and when dealing with the browser UI-level events, or the object model the browser exposes. We are able to keep that part of the problem contained away in specific classes that are targeted towards individual browsers. The compiler doesn't really have any first-hand knowledge of those differences. The GWT compile system brings in the appropriate classes for the browser, and that class contains the implementation details.
Frank Sommers: What happens to a Java class when it's compiled into JavaScript?
Scott Blum: JavaScript doesn't really have a well-defined class system: It's a prototype-based language. There are idioms that people follow in JavaScript to do things that are very similar to what you'd do in Java classes.
There is more flexibility with what you can do in JavaScript that, in some ways, I'd say is more rope [to hang yourself with]. The point, though, is that it's pretty easy to implement Java's ideas of classes in JavaScript, because JavaScript is slightly more flexible.
For example, if we have a Java class that we're translating into JavaScript, we'll create a class seed function, a constructor function, for that class. And then we add all the fields and polymorphic methods to that prototype, so that when you go to instantiate, say, a Button
in Java code, what you actually get is a call to that constructor function in JavaScript that returns the translated version of the class.
The methods pretty much are translated on a method-by-method basis. If you look at the un-obfuscated version of our compiler output and compare that to the original Java source code, there is a high degree of correlation. You could pretty much follow the output JavaScript and be able to figure out where in your source code that came from.
Frank Sommers: What are prototype-based objects?
Scott Blum: The way JavaScript works is that objects have what's called a prototype chain. When you're trying to look up some field foo
on a particular object [that is assigned to], say, some variable X
, the first thing [the JavaScript interpreter] does is look in X
to see if X
has a foo
.
If X
does, then you get whatever value happens to be there. If it doesn't, [the interpreter] walks up the prototype chain to X
's prototype object, and sees if it has one. It will walk up that chain [to] as many prototype parents as there are, until it gets to the end of the chain. If it hasn't found that still at the end, then you get an undefined
back.
We were able to take this construct and map on the Java idea of superclasses. For example, we set up the constructor function for Object
, and set up a prototype that has just hashCode
, equals
, and toString
. For any classes that we derive from that, we start with an instance of Object
, which becomes the prototype for some more specialized subclass, and so on down the chain.
Frank Sommers: What types of Java code are the hardest to translate to JavaScript?
Scott Blum: The most difficult aspect is that Java's exact behavior is very highly specified. For example, when you have a class that's being initialized in its constructor, there are rules governing when the superclass constructor gets called, when you can make polymorphic method calls to your subclass, evaluation order, things like that. There are a whole series of those issues that are precisely specified.
That makes it difficult to have an accurate compiler, because there are a lot of corner cases you have to worry about. Sometimes the most natural and efficient thing you might want to do doesn't actually end up doing the right thing. Most people would never run into most of those cases, but it does make writing a compiler quite difficult. If they left things a bit looser, a little more unspecified, most people would never know or care about them, but it would make implementing a correct compiler a lot easier.
Frank Sommers: You mentioned that browser-specific functionality is brought into the compile process from external libraries. Can you explain how that works?
Scott Blum: We have a general mechanism in the compile process that we call deferred binding. It gives you the ability to replace classes wholesale at compile time.Imagine that you have an abstract DOM class, and that you want that to be replaced at compile time with a concrete version that would be exactly tailored to the platform you're on. Through deferred binding, you set up rules in your project configuration that allow you to specify under what conditions particular classes get swapped in for other classes.
For a basic application, most developers wouldn't have to worry about this because that's already built into our libraries. It's part of how our metadata gets inherited. But this is something you can take advantage of if you provide browser-specific implementations of your own class library.
When the compiler goes to compile something, it looks at a set of properties and generates one permutation of the compile for each of the possible states of a property. For example, there is a user agent property that has one of four different values—Firefox, IE, Opera, or Safari. The compiler will generate four versions of your application, one tailored for each one of those.
If the first permutation is Firefox, it comes up with the Firefox implementation of the DOM as being the correct replacement class, and the next time around, it will come up with one for IE, and so on.
In addition to just being able to replace classes with already written classes, the other powerful facility is to generate classes on the fly during compilation. You can run a generator which will spit out new classes that can be subclasses of classes you've already defined in your project. That is how our RPC mechanism works. All the code to serialize and deserialize your object gets generated at compile time.
Frank Sommers: Once you have those browser-specific versions, how do you deploy them so that each browser gets only the code aimed for it?
Scott Blum: When the compiler generates each of the browser-targeted output files, it also generates something we call a selection script. That is a JavaScript file that runs on the client during the GWT bootstrap process, and runs any of the logic needed on the client browser to determine what their correct property values are.
Based on those property values, the script generates the request for one of the possible compiler output permutations. So a Firefox user will get the outerHTML
for the page, which will include this selection script. The selection script realizes it's running on Firefox, and that will cause it to request as its next script the right compiler output. As a user, all you really have to worry about is copying the output from your compiler directory to your Web server.
Frank Sommers: You said earlier that JavaScript is more flexible than Java. In what way do you take advantage of more flexibility in JavaScript to get, say, better performance, or more concise code?
Scott Blum: We definitely try to take advantage of tricks in JavaScript to make [the generated code] more efficient in both size and speed.
One thing to point out is JavaScript's support of the JSON format, which we use in a couple of places. In the compiler, we use that to be able to wrap and construct objects with a very small amount of syntax. It's actually quite fast. JSON has an object notation syntax that allows you to use an open brace, and then a bunch of identifiers, with colons separating them from their values, followed by a close brace. We can use that syntax to very quickly construct what an object would look like in code, instead of having to explicitly assign every single identifier.
The other place we use it in is our RPC—we use JSON as our RPC wire format. That allows us to very quickly serialize and deserialize things that go over the wire. That cuts down on the amount of processing that has to be done.
Frank Sommers: In addition to excellent Java developer tools, what do you think are the biggest differences for a developer between writing a browser-based application in Java versus writing that app directly in JavaScript?
Scott Blum: The biggest difference is not so much what the program might eventually do, or its general performance characteristics—although I think GWT has really good performance characteristics. To me, the real story is that when you write some large piece of code in JavaScript, the more [code] you write, the harder it is to maintain [that code], simply because it's not statically typed, and you don't get any real good IDE help or compiler help.
Also, [JavaScript code] is very hard to break down into functional chunks that you'll know will work well together through, for instance, well-defined interfaces. In Java, what you get is scalability, which comes into play the larger the project you're doing is, and the more people you have on it.
If you're going to sit down and write a small application, JavaScript is probably a fine choice. But the bigger the thing you try to do, the more leveraging the power of Java is really going to help you.
JavaScript is quite flexible and powerful. Most of its drawbacks, at least to me, come not from what you can express in it—because you can express a whole lot—it's just that it's all completely free-form, so to speak. The power of JavaScript is also rope that you can very easily hang yourself with.
That's why, when people are creating big class libraries, a lot of what you're doing is convention-based. You have a convention for how you construct a class, you have a convention for this or that, and there is no programmatic enforcement of any of those conventions.
Frank Sommers: What are the GWT compiler's current limitations in translating Java code to JavaScript?
Scott Blum: None of the Java 5 language features are supported right now. We're strictly 1.4-based in terms of source compatibility. In terms of the actual language itself, we pretty much support everything. Inner classes, anonymous classes are supported, for instance. Pretty much anything you can express is straight Java code, you can do in GWT.
The only real limitation of what Java code you can translate into JavaScript is that you have to have all the sources available. One reason you can't translate all of the Java I/O classes is that they have a bunch of native methods in them that are implemented in C++ code, so there is no way we could translate that. Also, a couple of things wouldn't make sense in a browser, like synchronization, because JavaScript is single-threaded. And we don't fully support the long data type because none of the browsers do. They all support double-precision floating-point, but don't have a 64-bit integer type.
The Google Web Toolkit
http://code.google.com/webtoolkit/
Artima Interviews Google's Bret Taylor on GWT
http://www.artima.com/forums/flat.jsp?forum=276&thread=185564
Have an opinion? Readers have already posted 8 comments about this article. Why not add yours?
Frank Sommers is a Senior Editor with Artima Developer. He also serves as chief editor of the IEEE Technical Committee on Scalable Computing's newsletter, and is an elected member of the Jini Community's Technical Advisory Committee. Prior to joining Artima, Frank wrote the Jiniology and Web services columns for JavaWorld.
Artima provides consulting and training services to help you make the most of Scala, reactive
and functional programming, enterprise systems, big data, and testing.