sulting in a minor size reduction. Even
better, the inlined code is amenable to
being further optimized in a usage-spe-cific context where more information
is available to the optimizer.
Next, the optimizer noticed that the
types of shape1 and shape2 could
be “tightened” to types more specific
than their original declaration. In other words, although shape1 was declared to be a Shape, the compiler saw
that it was actually a Circle. Similarly,
the type of shape2 was tightened to
Square. Consequently, the calls to
getArea() in [ 1] and [ 2] were made
more specific. The former became a
call to Circle’s getArea(), and the latter became a call to Square’s getArea(). Thus, all the method calls were
statically bound, and all polymorphism was removed.
Finally, with all polymorphism removed, the optimizer inlined Circle’s
getArea() into [ 1] and Square’s
getArea() into [ 2]. Both getArea()
methods are absent from the compiled
script, having been inlined away. Math.
PI is a compile-time constant and was
also trivially inlined into [ 1].
The benefit of all these optimizations is speed. The script produced
by the GWT compiler executes more
quickly because it eliminates multiple
levels of function calls.
For obvious reasons, large codebases tend to be written with an emphasis
on clarity and maintainability rather
than just on sheer performance. When
it comes to maintainability, abstraction, reuse, and modularity are absolute cornerstones. Yet, in the previous
example, maintainability and performance come into direct conflict: the
inlined code is faster, yet no software
engineer would write it that way. The
“maintainability vs. performance”
dichotomy isn’t unique to Java code,
of course. It is equally true that writing modular, maintainable JavaScript
tends to produce slower, larger script
than one would prefer. Thus, all developers building complex Web applications have to face the reality of
this trade-off. The pivotal question
would seem to be just how amenable
your codebase is to optimization once
you’ve written it. In that regard, the
Java type system provides great leverage, and that is how the GWT compiler
is able to include many optimizations
function runTest() {
var shape1, shape2;
shape1 = $Circle(new Circle(), 3);
shape2 = $Square(new Square(), 2);
alert(‘The area is ‘ +
shape1.radius shape1.radius
3.141592653589793); // [ 1]
alert(‘The area is ‘ + shape2.
length shape2.length); // [ 2]
}
Box 2. implementing native Java methods in handwritten JavaScript.
// This is Java!
static native Element createDivElement() /*-{
// This is JavaScript!
return document.createElement(“div”);
}-*/;
the CCL knows about JSNI and rewrites it to look something like this:
// A static initializer is introduced in the class.
static {
hostedBrowser.injectFunc(“createDivElement”, “return document.
createElement(\”div\”);”);
}
// The method becomes all-Java and is no longer native.
static Element createDivElement() {
return hostedBrowser.invokeInjectedFunc(this, “createDivElement”);
};
At [ 1], it’s impossible to tell statically
whether foo is a function or a variable,
so IDE code completion can only provide “potentially correct” suggestions,
which is an optimistic way of saying
that you must double-check the IDE’s
code completion suggestions, which
in turn is likely to diminish much of
the would-be productivity gain to be
realized from a JavaScript IDE. For
similar reasons, automated refactoring tools for JavaScript are rarely seen,
even while such tools are ubiquitous
in the Java world. These observations
made JavaScript seem less attractive as
a language in which to write large applications.
We finally realized that we wanted to
develop our source code in the Java language, yet deploy it as pure JavaScript.
By choosing the Java language as the
origination language, we could immediately leverage the great ecosystem
of Java tools, especially the great Java
IDEs out there. The only question was
how to produce JavaScript from the
Java source input. Our answer was to
build a Java-to-JavaScript compiler—
an optimizing compiler, in fact, because we figured that since we were going to the trouble of writing a compiler
anyway, why not make sure it produced
small, efficient JavaScript? Furthermore, we discovered that because Java
has a static type system, it allowed for
many compile-time optimizations that
JavaScript—being dynamically typed—
would not.
As an example of this, consider the
interaction between inlining and devir-
tualization (that is, the removal of polymorphism in a method invocation). In
JavaScript, developers often simulate
object-oriented constructs such as
polymorphism. Box 1, for example, illustrates how a simple Shape hierarchy
might appear written in JavaScript and
Java language for use GWT.
The source for the two examples
looks nearly identical, except for minor
syntax differences, the use of @Override (which is useful for helping to
prevent bugs), and the presence of explicit type names sprinkled on fields,
methods, and local variables.
Because of the extra type information, the GWT compiler is able to perform some optimizations. The unobfuscated version of the GWT compiler
output looks approximately like this:
Note that in [ 1] and [ 2] , a cascade of
optimizations was made.
First, the compiler inlined both calls
to the displayArea() method. This
proved helpful because it removed the
need to generate code for that method.
Indeed, displayArea() is completely
absent from the compiled script, re-