Relaxing constraints on GWT.create()

37 views
Skip to first unread message

Ray Cromwell

unread,
Mar 12, 2009, 6:22:36 PM3/12/09
to GWTcontrib
I just put up a new blog post on a proposed extension to the rules
surrounding GWT.create(), see here:
http://timepedia.blogspot.com/2009/03/relaxing-constraints-on-gwtcreate.html

The basic goal is to allow the creation of static utility methods that
can narrow the type signature of GWT.create(), to decorate, wrap, or
enhance (at runtime) the returned object, and to override the
generator at the declaration site. I think this allows an improvement
in usability and safety surrounding deferred binding, in particular:

1) trapping type errors earlier (edit time, javac compile-time) with
narrower parameters and return types
2) JavaDoc for your own create methods explaining the function of the
generator and what it will return. (currently, this documentation must
reside in the interface)
3) runtime decoration of returned types (assertions on runtime state,
wrappers, injection of runtime state, etc)
4) shorter and simpler invocation code in some circumstances, arguable
readability improvement

I'm wondering if anyone else likes this idea?

-Ray

Ian Petersen

unread,
Mar 12, 2009, 8:49:25 PM3/12/09
to Google-Web-Tool...@googlegroups.com
Seems like this might be related to
http://code.google.com/p/google-web-toolkit/issues/detail?id=2243

I like the idea.

Ian

Ray Cromwell

unread,
Mar 12, 2009, 10:17:42 PM3/12/09
to Google-Web-Tool...@googlegroups.com
Yeah, it's mostly the same as was discussed in the past. I just
decided to resurrect the idea given that 1.6 is almost shipped to
ensure it has a good chance of making it into the next version.

-Ray

Ray Cromwell

unread,
Mar 13, 2009, 12:23:58 AM3/13/09
to Google-Web-Tool...@googlegroups.com
BTW,
My proposed implementation is to rewrite these methods changing the
literal parameter to the result of invoking GWT.create() at the call
site, e.g.

interface MyService extends RemoteService<MyServiceAsync> { ... }
// example call
OAuth.withSignature(MyService.class).method1(arg1, arg2, asyncCallback);

public class OAuth {
@GwtCreate // method must be static, class parameter must be final
public static <S, T extends RemoteService<S>> S withSignature(final
Class<T> service) {
// GWT.create with non-literal ONLY allowed
// if enclosing method is @GwtCreate and
// variable statically resolvable to literal param
S async = GWT.create(service);
((ServiceDefTarget)async).setRequestBuilderCallback(new
OAuthRequestBuilderSigner());
return async;
}
}

after rewrite at callsite:
Object async = GWT.create(MyService.class);
OAuth.withSignature(async).method1(arg1, arg2, asyncCallback);

rewritten method:
public class OAuth {
@GwtCreate // method must be static, class parameter must be final
public static <S, T extends RemoteService<S>> S withSignature(Object
asyncInstance) {

//S async = GWT.create(service); replaced with instance
S async = (S)asyncInstance;
((ServiceDefTarget)async).setRequestBuilderCallback(new
OAuthRequestBuilderSigner());
return async;
}
}

This doesn't handle generator override or extra params, but it's a
start and could work with minimal changes to ReplaceRebinds.

-Ray

Matt Mastracci

unread,
Mar 13, 2009, 1:55:29 AM3/13/09
to Google-Web-Tool...@googlegroups.com
+1 for this implementation.

This is a lot clearer than a class -> implementation lookup map or
inlining the whole method at each call site. As long as the bytecode
is rewritten in hosted mode to match the expected order of operations,
there shouldn't be any surprises at compile time.

Could this cause issues with anyone running GWT.create() in server-
side code? Does that even work today?

Ray Cromwell

unread,
Mar 13, 2009, 2:09:38 AM3/13/09
to Google-Web-Tool...@googlegroups.com
One concern I have is order of operations, since the GWT.create() is
implicitly hoisted from its callsite, and I could see some crazy thing
where code in the method depends on the deferred binding not being
initialized early. It's kinda like the clinit() hoisting problem. I
say we just define some rules that allow hoisting within a method
body, otherwise, you'll have to inline the whole method at the
callsite (yuck!)

-Ray

Matt Mastracci

unread,
Mar 13, 2009, 2:23:14 AM3/13/09
to Google-Web-Tool...@googlegroups.com
If you wanted to be guaranteed order-of-operations safety, you could
pass a factory into the method instead of the object itself and
replace the GWT.create() calls with a create() method on the factory.
AFAICT, this would have the same sequence of side-effects as
GWT.create() would.

Bonus feature: In the case where only one class constant is ever
passed into this method, the compiler should be able to replace the
virtual call with the new MyService() expression (and possibly inline
the whole @GwtCreate function as well).

For instance, something like the following pseudocode:

interface GwtCreateFactory {
<T> T create();
}

class MyService__GwtCreateFactory implements GwtCreateFactory {
@SuppressWarnings("unchecked")
<T> T create() {
return (T)new MyService();
}
}

public class OAuth {
@GwtCreate // method must be static, class parameter must be final
public static <S, T extends RemoteService<S>> S withSignature(final
GwtCreateFactory<T> service) {
// GWT.create with non-literal ONLY allowed
// if enclosing method is @GwtCreate and
// variable statically resolvable to literal param
S async = service.create();
((ServiceDefTarget)async).setRequestBuilderCallback(new
OAuthRequestBuilderSigner());
return async;
}
}

OAuth.withSignature(new MyService__GwtCreateFactory()).method1(arg1,
arg2,
asyncCallback);

Ray Cromwell

unread,
Mar 13, 2009, 2:45:06 AM3/13/09
to Google-Web-Tool...@googlegroups.com
Matt,
Great idea, and has the added bonus of making the hosted mode and
web mode behavior roughly identical. One of my design goals for the
implementation is to minimize the area of the compiler/code base
affected by using mostly Java syntactic sugar transformations. I had a
similar idea with respect to parameterized GWT.create() calls,
consider:

Gin.inject(Foo.class, FooModule.class);

public class Gin {
@GwtCreate(generator=com.google.guice.rebind.GinInjectorGenerator.class)
public static <S, T extends AbstractModule> S inject(Class<S> intf,
@GwtCreateParam T module) {
return GWT.create(intf, module);
}
}

We transform this by creating a subtype of Foo.class with the
parameters transformed into annotations.

@GwtCreateBindings({@BoundLiteral(FooModule.class)})
interface Foo$FooModule extends Foo {

}

and rewrite the GWT.create() call as

GWT.create(Foo$FooModule.class);

Then generators can access the parameters by looking up
GwtCreateBindings on the requested type.

If there is a call to Gin.inject(Foo.class, BarModule.class) you
simply get another interface like

interface Foo$BarModule extends Foo { ... }

It might be cleaner to add true support for a var-args version of
GWT.create() with changes to GeneratorContext, but that would touch on
alot more of the codebase than I have a grasp of, requiring more
proof of it working properly, and not breaking existing code.

-Ray
Reply all
Reply to author
Forward
0 new messages