Method removal issue and reflection

8 views
Skip to first unread message

Zuhayr Khan

unread,
Dec 30, 2006, 3:43:47 AM12/30/06
to Google Web Toolkit Contributors
Hi,

It was suggested that I post this to this group instead of the general
Google Web Toolkit group, so apologies to those who may have read it
there.

I've looked through various posts around this subject, which have been
helpful, however I'm not able to use any work-arounds suggested.

I have Swing GUI application that I want to also render through dynamic
web pages. I am keen to do this with as much of the same code base as
possible.

I have factored the application to follow the MVC pattern and have
emerged with a set of fairly thin view classes. My idea is to have GWT
versions of these view classes sit alongside their Swing equivalents
and that this should be the only difference between the deployments of
the app to desktop and web users.

I have written some test classes in GWT as well as some framework
classes. I have got over the issue of not being able to "push" changes
out to a UI in the way that you do with setNNN() type calls in Swing by
having a GWT class use an AJAX thread to pull back a list of "changes"
(i.e. details of setNNN() calls) and realise them on the local
(Javascript/HTML) interface.

In order to do this, I use a pseudo-reflection-type mechanism in a GWT
class...

public static native void invokeNativeMethod(Object current, String
methodName, Object arg0) /*-{
this["_$"+methodName](current, arg0);
}-*/;

... I know this requires me to tweak the GWT compiler with -style
PRETTY so the method names remain - however this is not my biggest
problem at present.

Because the GWT Compiler removes any un-referenced methods; I'm forced
to introduce an "init" stage to my view class so that when the
invocation of setNNN() occurs, there's a method there to invoke.

This is all very well in my simple test view class; however in even a
modest dialog in the app, this is going to be a substantial "init"
method which doesn't actually buy me anything from a code perspective,
and is going to be difficult to keep in synch with future changes.

Ideally; I'm looking for some way in which to mark the setNNN() methods
in my view class as "do not remove" that doesn't require me to
reference the methods in an init method (or other static-referencing
mechanism). All these methods exist in an interface that I have defined
for my view which both Swing_VIEW_IMPL and GWT_VIEW_IMPL implement, so
I guess what I'm asking for might be something along the lines of an
extra directive in the .gwt.xml file... so that I could write something
like:

<entry-point class='GWTViewOneImpl' honour-interfaces='ViewOne,
ViewOneA' />

..and that the compiler would mark any methods in GWTViewOneImpl that
are implementations of ViewOne or ViewOneA as 'not to be removed'

Of course the other thing I really ought to deal with is a nicer way in
which to still be able to invoke these methods by their
non-pretty-name, but I think I need to solve this problem first.

If anyone has any alternative suggestions as to how I might accomplish
what I'm trying to do, I'd be very grateful, or if this
"honour-interfaces" sounds reasonable please let me know - I'm happy to

help with any dev work for this if it's a popular and workable
solution.

I hope not to have given too much detail, but if any more detail is
required, please reply on this thread.

Thanks in advance,

Zuhayr

Bruce Johnson

unread,
Jan 11, 2007, 12:51:28 AM1/11/07
to Google-Web-Tool...@googlegroups.com
Your post was clear, but I missed one thing: why doesn't the compiler think you're calling those setNNN() methods to begin with? Normally, you'd have code that references them in one way or another that the compiler should recognize as being callable.

-- Bruce

John Tamplin

unread,
Jan 11, 2007, 12:56:22 AM1/11/07
to Google-Web-Tool...@googlegroups.com
On 1/11/07, Bruce Johnson <br...@google.com> wrote:
Your post was clear, but I missed one thing: why doesn't the compiler think you're calling those setNNN() methods to begin with? Normally, you'd have code that references them in one way or another that the compiler should recognize as being callable.

Because the method call is synthesized from a method name passed into a javascript function.

--
John A. Tamplin
Software Engineer, Google

Bruce Johnson

unread,
Jan 11, 2007, 1:57:13 AM1/11/07
to Google-Web-Tool...@googlegroups.com
[Okay, duh. Thanks, John.] So you never call those methods explicitly anywhere in your client code...hmmm...

=== Part 1 of suggestion ===
Let's take a step back. Zuhayr, you *really* should try to avoid having to compile without obfuscation. Here's one approach:

  private void fakeReflection() {
    JavaScriptObject disp = initDispatcher();
    invoke(disp, "dog", "ruff");
    invoke(disp, "cat", "meow");
  }
  
  private native void invoke(JavaScriptObject disp, String name, String arg) /*-{
    disp[name].call(this, arg);
  }-*/;
  
  private native JavaScriptObject initDispatcher() /*-{
    return {
      dog: this.@com.google.gwt.sample.hello.client.Hello::dog(Ljava/lang/String;),
      cat: this.@com.google.gwt.sample.hello.client.Hello::cat(Ljava/lang/String;)
    };
  }-*/;

  private native void dog(String msg) /*-{
    window.alert("dog says " + msg);
  }-*/;

  private native void cat(String msg) /*-{
    window.alert("cat says " + msg);
  }-*/;

Hopefully the above makes the pattern clear. The idea is that you reference JSNI methods as lambdas that you explicitly assign to variables with well-known names -- in this case, into a <name, lambda> map called "disp". Using this pattern will allow you to get the same effect but with obfuscation turned on. It's a little more code, but it's a lot more structured and obfuscation makes a huge difference in output size.

=== Part 2 of suggestion ===
The above manual step of creating a dispatcher could get unwieldy, depending on how many methods you need to expose in this manner. If there are many, then you could create a deferred binding Generator that generates those dispatch maps for you. In essence, the generator would allow you to selectively control which methods become callable using this simplistic form of reflection.

Hope that helps.

-- Bruce

Zuhayr Khan

unread,
Jan 15, 2007, 1:31:09 PM1/15/07
to Google Web Toolkit Contributors
Thanks for that guys - the dispatcher pattern looks good, but I think
it would become unwieldy quickly because I'd like to plug this into an
existing application which already has extensive interfaces.

In terms of a programmatic dispatcher, I'm not sure where this would
best be done. I suspect to do it within Java code destined for
compilation to Javascript might be a problem as I don't think there's
support for the Method class etc..

Putting a form of dispatcher into the compilation stage I guess is what
I was suggesting when I talked about "honour-interfaces".

I've worked on this as little in the GWT compiler code and although
I've had to modify a few ideas that I had originally I've managed to
take this forward to some extent.

I've defined my entry-point with:

<entry-point class='viewone.GWTViewOneImpl'>
<honoured-interface name='sampleclient.client.ViewOne'/>
</entry-point>

... and added the appropriate code in ModuleDef and ModuleDefSchema for
honoured-interface.
In the compile() method in JavaToJavaScriptCompiler I've added a call
to a new method that I wrote that looks for each method defined in the
referenced-interface and adds them to a store in the JProgram.

Then I've added code into the Pruner that rescues the specified methods
etc. At the moment I've also built a static method to delegate to this
rescued method (so I can "derive" the method name easily) - but I
suspect a mechanism closer to your dispatcher's philosophy would be
better so I can get back to a situation where I can turn on
obfuscation.

The code fell out fairly nicely and it fits what I consider a nice
philosophy which is not to repeat the "specification" of the methods
(beyond the interface) in order to specify these methods as 'available'
so if it's deemed useful by others, I'd be happy to contribute this
work - with some guidance over the naming side of things.

To be clear as to what I'm suggesting I'll summarize:

In order to provide a mechanism for declaring that some methods defined
in a Java class that is destined to be compiled into JavaScript, I
propose the capability to specify any number of "honoured-interface"
elements within a defined "entry-point" such that all defined methods
(and their associated code-paths) are prevented from being removed from
the generated program.
Use of the mechanism will allow for run time programatic method
invocation similar to:

myMethod.invoke(obj, new Object[] { arg1, arg2 });

... even when myMethod() has no other direct reference to it in the
class.

Hope this is clear; let me know what you think - I'm happy to discuss
changes/alternatives to this if you think there are ways better aligned
to GWT's goals.

Zuhayr

Scott Blum

unread,
Jan 15, 2007, 10:20:16 PM1/15/07
to Google-Web-Tool...@googlegroups.com
Hi Zuhayr,

To give some context on this, our general design philosophy is to not
put special magic into the compiler, if for no other reason than it
just doesn't scale. So we see if there are alternatives to specific
compiler changes that would do what we want.

In your case, here are what I see as the most relevant points:
1) You want to preserve certain methods from being pruned.
2) You want a way ot call them programmatically via string lookup.
3) You know the set of interfaces comprising the methods you care
about at compile time.

To me, this looks like a job for deferred binding with a generator.
Here's the basic plan that I think would do what you want:

abstract class Dispatcher {
abstract void invoke(Object thisObj, String methodName, Object[] args);
}

Then you add some deferred binding rules to use a generator on this
type to your module:

<generate-with class="com.example.dispatch.rebind.DispatchGenerator">
<when-type-assignable class="com.example.dispatch.client.Dispatcher"/>
</generate-with>

To use the service, the user could create an abstract subclass
specified to implement all of the interfaces they care about:

abstract class MyFooBarDispatcher extends Dispatcher implements Foo, Bar {
}

In this case, Foo and Bar are markers for the interfaces you want to
delegate to.

You would create the object as such:
Dispatcher myFooBarDispatcher =
(Dispatcher)GWT.create(MyFooBarDispatcher.class);

Now the DispatcherGenerator class would simply be responsible for
inspecting the user-specific MyFooBarDispatcher class and producing a
Dispatcher subclass that implements the invoke() method and can
delegate to any of the specified interfaces.

Scott

Zuhayr Khan

unread,
Jan 17, 2007, 12:25:22 AM1/17/07
to Google Web Toolkit Contributors
Thanks for that Scott - I see the virtue in keeping this kind of thing
in Generators.

I've begun looking at how I should implemenet the Generator, I'm
investigating whether I'll need to create sub-classes of Dispatcher for
each of the classes I want to use the dispatcher in as you suggest, or
whether I could get away with a single Dispatcher class and put more
intelligence in the Generator.

Given that my GWTViewOneImpl class already implements ViewOne; I'd like
to not have to duplicate this information in ViewOneDispatcher if I can
help it.

If I have a single Dispatcher class, as a Generator I would need to
understand that I am generating code on behalf of the class (e.g.
GWTViewOneImpl) in which I've got:

Dispatcher myFooBarDispatcher =
(Dispatcher)GWT.create(MyFooBarDispatcher.class);

.. looking through the context available to the Generator.generate() I
can't see an obvious reference to the Cud (or some other reference)
that would allow me to find the class I'm generating code on behalf of
... am I missing something or is subclassing Dispatcher the only way
for me to reference the interfaces.

Zuhayr

Zuhayr Khan

unread,
Jan 17, 2007, 1:19:12 AM1/17/07
to Google Web Toolkit Contributors
Ok, I think I've found a way around this; I can add a
<define-property/> element to my .gwt.xml file to point it to the
interface I want to honour; this is then available to me in the
PropertyOracle that I have access to in the Generator.generate()
method.

One thing I did find however, was that although the ModuleDefSchema
etc. seems to allow for comma-separated values; by the time the
property is set in the PropertyOracle, it only had my first value.

I think I can live with the restriction that you can only honour one
interface because I can have it extend any component interfaces, or am
I missing something subtle in the definition of a property?

Scott Blum

unread,
Jan 17, 2007, 11:36:05 AM1/17/07
to Google-Web-Tool...@googlegroups.com
On 1/17/07, Zuhayr Khan <Zuhay...@mac.com> wrote:
> If I have a single Dispatcher class, as a Generator I would need to
> understand that I am generating code on behalf of the class (e.g.
> GWTViewOneImpl) in which I've got:
>
> Dispatcher myFooBarDispatcher =
> (Dispatcher)GWT.create(MyFooBarDispatcher.class);
>
> .. looking through the context available to the Generator.generate() I
> can't see an obvious reference to the Cud (or some other reference)
> that would allow me to find the class I'm generating code on behalf of
> ... am I missing something or is subclassing Dispatcher the only way
> for me to reference the interfaces.

Unless I'm misunderstanding you, I think you might be on the wrong
track. The name of the class you are "generating code on behalf" of
is the last parameter to Generator.generate(). That is, in the above
sample code the String passed into Generator.generate() would be the
fully-qualified class name of MyFooBarDispatcher. To get the JType
for that class, call typeOracle.findType(requestedClass). You can
then use the JType API to find out what interfaces the requested type
implements.

Scott

Zuhayr Khan

unread,
Jan 17, 2007, 2:58:38 PM1/17/07
to Google Web Toolkit Contributors
Perhaps my naming of things is confusing; I understand that in the
situation where I have:

GWT.create(MyFooBarDispatcher.class);

This will result in the fully-qualified class name being passed to
generate() - this would all work fine - but what I was hoping to do was
to have just one Dispatcher class and hence...

GWT.create(Dispatcher.class);

... then infer from the fact that this GWT.create() line is within
class GWTViewOneImpl.class that the interface(s) that I should
"implement" are those which GWTViewOneImpl implements.

As I mentioned; I've addressed this at present with a define-property

I was only wondering if while my Generator.generate() call was in
progress; could I use some context (GeneratorContext?) to discover the
class that had the GWT.create().

Still looks a bit esoteric I guess, but hopefull it's a bit clearer

Scott Blum

unread,
Jan 17, 2007, 4:45:45 PM1/17/07
to Google-Web-Tool...@googlegroups.com
> I was only wondering if while my Generator.generate() call was in
> progress; could I use some context (GeneratorContext?) to discover the
> class that had the GWT.create().
>
> Still looks a bit esoteric I guess, but hopefull it's a bit clearer

There is no way to infer what class made the GWT.create() call, by
design. One reason for this is that deferred binding rules are only
run once per target class per compilation. You might have 20
GWT.create(Foo.class) calls in your app, but the deferred binding
rules and generation will only happen once for Foo.


> What I was hoping to do was to have just one Dispatcher class and hence...


>
> GWT.create(Dispatcher.class);
>
> ... then infer from the fact that this GWT.create() line is within
> class GWTViewOneImpl.class that the interface(s) that I should
> "implement" are those which GWTViewOneImpl implements.
>
> As I mentioned; I've addressed this at present with a define-property

While I'm sure you can make this go, I would strongly advocate against
such a design. There really isn't a good reason to restrict the user
to only using your Dispatcher as a one-shot deal per app.

Scott

Zuhayr Khan

unread,
Jan 18, 2007, 12:46:03 AM1/18/07
to Google Web Toolkit Contributors
Ok, thanks for the clarification - I didn't strongly want to use just
one Dispatcher class; I was only wanting to see what possibilities
there were for avoiding duplication, however; a number of light-weight
Dispatcher subclasses isn't too big a price to pay.

Thanks for your help with this - much appreciated.

Zuhayr

Reply all
Reply to author
Forward
0 new messages