Don't obfuscate Java methods when passing instance to JavaScript

75 views
Skip to first unread message

jarrod

unread,
Jun 2, 2009, 10:07:41 PM6/2/09
to Google Web Toolkit
I want to write a small "library" API which will expose certain
classes of Objects and well-known methods on them for plain-old
JavaScript to invoke.

Creating the API isn't too hard, and using JSNI, I'm able to create
methods in JavaScript that call back to the GWT (obfuscated) code.
What I haven't figured out how to do is prevent the instance methods
of a Java object passed to JavaScript from being obfuscated.

Say I have a Java class:

class Foo {

public void doBar() {
// ... something
}

}


Now in my EntryPoint class, I'll add some JSNI to expose the Foo
constructor:

public native void onModuleLoad() /*-{
$wnd.Foo = function() {return @Foo::new()();}
}-*/;


Okay, that all works great. And now from JavaScript, I can do this:

var f = new Foo();


Great. Now I have an instance of Foo. But how can I be certain that
'f' will have a 'doBar()' function?

f.doBar();

????

jarrod

unread,
Jun 2, 2009, 10:13:30 PM6/2/09
to Google Web Toolkit
One thing I tried was adding an 'export' method to the Foo class:

class Foo {

public Foo() {
this.export();
}

private native void export() /*-{
this.doBar = function() {this.@Foo::doBar()();}
}-*/;

public void doBar() {
// ... something
}
}


This causes, on creating an instance of Foo, the appropriate API
methods to be exposed in JavaScript. However, when running in hosted
mode, I get all kinds of Malformed JSNI Exceptions, whining about how
the 'doBar' field is not found.


[WARN] Malformed JSNI reference 'doBar'; expect subsequent failures
java.lang.NoSuchFieldError: doBar
at com.google.gwt.dev.shell.CompilingClassLoader
$DispatchClassInfoOracle.getDispId(CompilingClassLoader.java:122)
at com.google.gwt.dev.shell.CompilingClassLoader.getDispId
(CompilingClassLoader.java:574)
at com.google.gwt.dev.shell.mac.WebKitDispatchAdapter.setField
(WebKitDispatchAdapter.java:106)
at com.google.gwt.dev.shell.mac.LowLevelSaf.invokeImpl(Native Method)
at com.google.gwt.dev.shell.mac.LowLevelSaf.invoke(LowLevelSaf.java:
158)
at com.google.gwt.dev.shell.mac.ModuleSpaceSaf.doInvoke
(ModuleSpaceSaf.java:100)
at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:
453)
at com.google.gwt.dev.shell.ModuleSpace.invokeNativeVoid
(ModuleSpace.java:251)
at com.google.gwt.dev.shell.JavaScriptHost.invokeNativeVoid
(JavaScriptHost.java:107)
at Foo.export(OAuthLogonService.java)
at Foo.<init>(OAuthLogonService.java:29)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native
Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance
(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance
(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:494)
at com.google.gwt.dev.shell.MethodAdaptor.invoke(MethodAdaptor.java:
105)
at com.google.gwt.dev.shell.mac.MethodDispatch.invoke
(MethodDispatch.java:71)
at org.eclipse.swt.internal.carbon.OS.SendEventToEventTarget(Native
Method)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:2914)
at com.google.gwt.dev.SwtHostedModeBase.processEvents
(SwtHostedModeBase.java:235)
at com.google.gwt.dev.HostedModeBase.pumpEventLoop
(HostedModeBase.java:558)
at com.google.gwt.dev.HostedModeBase.run(HostedModeBase.java:405)
at com.google.gwt.dev.HostedMode.main(HostedMode.java:232)



This works no problem compiled in the browser, but it would be nice if
hosted mode would continue to work... As it is, it just bombs and
stops and doesn't go any further than the exception.

Dean S. Jones

unread,
Jun 2, 2009, 11:43:52 PM6/2/09
to Google Web Toolkit
Short answer, check out:

http://code.google.com/p/gwt-exporter/

jarrod

unread,
Jun 3, 2009, 9:35:23 AM6/3/09
to Google Web Toolkit
I did check it out, but unfortunately, there seems to be a bug in the
framework preventing me from doing what I want.

I'm trying to follow up with that group on the issue, but the long and
short is that (trust me on this) I couldn't get it working with my
code.

However, after looking through the gwt-exporter source code a bit, I
am encouraged that this sort of thing is possible - I just can't crack
how to do it without the aid of a third-party library.

Thanks. Any other suggestions?

Dean S. Jones

unread,
Jun 3, 2009, 10:24:36 AM6/3/09
to Google Web Toolkit
This is from memory, but in general, I thing it should work:

package com.mypackage;

public class Foo
{
private String message;

public Foo(String message)
{
this.message = message;
}

public void sayHello()
{
Window.alert("Hello " + message);
}

public static native void export() /*-{

function Foo_Type(message)
{
this.instance = @com.mypackage.Foo::new(Ljava/lang/String;)
(message);
}
Foo_Type.prototype.sayHello = function
{ this.instance.@com.mypackage.Foo::sayHello()(); }

$wnd.Foo = Foo_Type;
}-*/;
}

Call Foo.export() from your onModuleLoad()

JavaScript should then be able to do:

var f = new Foo("Dean");

f.sayHello();

jarrod

unread,
Jun 3, 2009, 12:56:46 PM6/3/09
to Google Web Toolkit, Dean S. Jones
I think I can see what you're saying... But what about objects that
are not created in JavaScript? Suppose the object/class in question is
instantiated in the Java world and passed over to JSNI?

jarrod

unread,
Jun 3, 2009, 12:58:08 PM6/3/09
to Google Web Toolkit, Dean S. Jones
Of course this also means that an instance created in JavaScript and
passed into Java is not an instance of com.mypackage.Foo... it's a
JavaScriptObject.


Good points, though.



On Jun 3, 10:24 am, "Dean S. Jones" <deans...@gmail.com> wrote:
Reply all
Reply to author
Forward
0 new messages