Unreferenced methods get removed during compilation

42 views
Skip to first unread message

Greg Blomquist

unread,
Jul 11, 2006, 9:19:58 AM7/11/06
to Google Web Toolkit
I've run into something that has got me terribly confused. I have been
working on a GWT-based application for a few weeks and everything has
been going great. Then, I decided to add some "hook" methods that
could be easily called from outside javascript methods to kick off some
of the Ajax methods in my code. The purpose behind these hook methods
is to allow legacy pages to include functionality from my GWT
application. The nice thing about Ajax is that using it doesn't
require a dynamic (i.e., server-side) page.

The trick with these hook methods is that no code in my GWT application
actually calls them. That's the point, they're being made available
for outside sources to call them. However, when I compile my app, the
unreferenced methods get removed, or don't get compiled.

I've tested this by having the following:

// simple method that is referenced by code in my GWT application
public void gwtReferencedMethod(String arg) {
Window.alert(arg);
}
// simple method that is not referenced in my GWT application
public void unreferencedMethod(String arg) {
Window.alert(arg);
}

When I compile the above code in my application, the
gwtReferencedMethod is compiled into Javascript, but unreferencedMethod
is not compiled into Javascript. However, I can change the code to the
following:

// simple method that is referenced by code in my GWT application
public void gwtReferencedMethod(String arg) {
unreferencedMethod(arg);
}
// simple method that is now referenced in my GWT application
public void unreferencedMethod(String arg) {
Window.alert(arg);
}

When I change to the above code, both methods are compiled into
Javascript. I've tested this several times to be sure my eyes weren't
playing tricks on me.

Is this a "feature" of the GWT Compiler? If so, is it documented
anywhere? Is it done for optimization? Or, for some obscure security
reason? Most importantly, is there a way to turn it off?

----
Greg

Roman Kramar

unread,
Jul 11, 2006, 9:30:41 AM7/11/06
to Greg Blomquist
Hello Greg,

It looks like an optimization to reduce the size of the resulting
Javascript code and it makes sense to me. I see your point though. But
how were you going to call GWT methods from you legacy Javascript
code? GWT produces rather obfuscated code by default and to me it will
not be easy to even find the needed methods there. You can tell the compiler
to generate more readable output, but that will dramatically increase
the size of your client side code.

Regards,
Roman

br...@google.com

unread,
Jul 12, 2006, 12:29:37 AM7/12/06
to Google Web Toolkit
The GWT compiler does indeed remove as much code as it possibly can,
including discarding uncalled methods and unused classes. The size and
speed savings are tremendous, so it's a very important feature.

However, we knew that people would want to do what you're describing,
so there's a special case: any Java method called from within a JSNI
method that itself gets called will not be discarded. It's up to you to
decide how you want to guarantee that the JSNI method gets called in
the first place; an easy approach is to call a "init()" function from
within a module entry point.

That's the terse description. An example will hopefully make more
sense...

=== BEGIN
package foo;

public class MyEntryPoint implements EntryPoint {
public void onModuleLoad() {
initJavaScriptApi();
}

private native void initJavaScriptApi() /*-{
// define a static JS function with a friendly name
$wnd.sayHello = function () { @foo.MyEntryPoint::sayHello()() };
}-*/;

// This method will not be discarded since
// (1) initJavaScriptApi() calls this method from JSNI
// (2) initJavaScriptApi() is definitely called
public static void sayHello() {
Window.alert("hello");
}
}
=== END

Now in your host html you can write JavaScript using the "pretty"
identifiers you assigned in the initJavaScriptApi() method above, like
this:

// regular-old JavaScript
function someJavaScriptFunction() {
sayHello();
}

This technique lets you create JavaScript APIs that call into your
compiled Java code while your Java code is still completely optimized
and obfuscated -- the best of both worlds.

-- Bruce

P.S. I didn't actually run the code snippet above, so there could be
minor errors. It's really meant to show the pattern rather than to be
copied/pasted.

Roman Kramar

unread,
Jul 12, 2006, 2:06:16 AM7/12/06
to Google-We...@googlegroups.com
Hi Bruce,

> This technique lets you create JavaScript APIs that call into your
> compiled Java code while your Java code is still completely optimized
> and obfuscated -- the best of both worlds.

Cool! I didn't think of that. It's really a nice way to do this sort
of things. Thanks for pointing us to it!

Regards,
Roman

mP

unread,
Jul 12, 2006, 7:14:14 AM7/12/06
to Google Web Toolkit
Create a "clientside java" static method to kick everything off. This
method will be the interface for your normal javascript. Create another
method to read this method into a known variable under $wnd.

Using JSNI assign a method ( in the same way one reads a java static
field) to a javascript variable and stick it somewhere for your regular
javascript to find and execute.
Because its static there is no issues regarding this etc.

I have done something similar to hte above and it seems to take care of
the unreferenced method getting excluded from the generated js.

HTH

mP

Greg Blomquist

unread,
Aug 2, 2006, 7:41:49 PM8/2/06
to Google Web Toolkit
Thanks Bruce!

That's exactly what we ended up doing. I think we actually did that on
the same day I made the post. I probably should have checked back here
sooner and indicated that we had a workable solution.

Again, thanks for the reply. I'm extremely impressed by the
responsiveness of your team of developers and support people. Keep up
the good work!

adamp

unread,
Aug 28, 2006, 10:34:15 PM8/28/06
to Google Web Toolkit
I've got the basics to work as outlined above, but I want to be able to
pass params in and return values out. Unfortunatetly, I can't quite
seem to get the JSNI correct. So I modified the example so that
sayHello takes a string argument.

===BEGIN
package foo;


public class MyEntryPoint implements EntryPoint {
public void onModuleLoad() {
initJavaScriptApi();
}


private native void initJavaScriptApi() /*-{
// define a static JS function with a friendly name
$wnd.sayHello = function () {

@foo.MyEntryPoint::sayHello(Ljava/lang/String;)(s) };
}-*/;


// This method will not be discarded since
// (1) initJavaScriptApi() calls this method from JSNI
// (2) initJavaScriptApi() is definitely called

public static void sayHello(String s) {
Window.alert(msg);
}


}
===END


// regular-old JavaScript
function someJavaScriptFunction() {

sayHello("hello");
}

But could not get it to work. Could someone clarify the parameter
specification??

Thanks in advance.

Vivian Li

unread,
Aug 30, 2006, 2:24:46 AM8/30/06
to Google-We...@googlegroups.com

Hi adam,
  in your initJavaScriptApi() method, I believe you JavaScript has an error:


 $wnd.sayHello = function () {
@foo.MyEntryPoint::sayHello(Ljava/lang/String;)(s) };

should be

 $wnd.sayHello = function (s) {
@foo.MyEntryPoint::sayHello(Ljava/lang/String;)(s) };


-Vivian




Reply all
Reply to author
Forward
0 new messages