Inaccessible (manually created) JavaScript variable from JSNI

150 views
Skip to first unread message

eliasb...@gmail.com

unread,
Jan 4, 2008, 5:44:45 PM1/4/08
to Google Web Toolkit
Hello and happy new year everyone.

I am building a DWR integration library for GWT (http://
code.google.com/p/dwr4gwt) based on the assumption that I can bind to
my server-side exposed interfaces whose definitions are available as
under http://server:port/app/dwr/interface/XXX.js (where dwr is
mapped to a special servlet that serves the interface definition as
JavaScript)

DWR client-side engine is available under dwr/engine.js (same special
servlet involved).
So far so good. A test with a simple HTML page calling DWR with the
above scripts has successfully worked.

Next step is teaching a GWT widget do the same trick.
Thus, I included the following definitions in the HTML file of my GWT
module (right under gwt.js inclusion)

<script language="javascript" src="../dwr/engine.js"></script>
<script language="javascript" src="../dwr/interface/
RemoteFunctions.js"></script>

expecting that the variables dwr and RemoteFunctions (found in
engine.js and RemoteFunctions.js) will be available in GWT widget JSNI
call like

private native void jsCallRemoteMethod(
String remoteClassName,
String remoteMethodName,
JavaScriptObject callParameters,
int remoteMethodCallDescriptorKey,
int callTimeout) /*-{
var me = this;
var remoteClass = eval(remoteClassName);
var remoteMethod = eval(remoteClassName+'.'+remoteMethodName);
var callMetadata = {
callback:
function(returnValue) {

me.@com.eliasbalasis.dwr4gwt.client.DwrAdapter::fireRemoteMethodCallReply
(Lcom/google/gwt/core/client/JavaScriptObject;I)
(
returnValue,
remoteMethodCallDescriptorKey
);
},
timeout:
callTimeout,
errorHandler:
function(errorMessage) {

me.@com.eliasbalasis.dwr4gwt.client.DwrAdapter::fireRemoteMethodCallError
(Ljava/lang/String;I)
(
errorMessage,
remoteMethodCallDescriptorKey
);
}
};
...
...
...
...
...
}-*/;
(where remoteClassName above is the name of the JavaScript object
defined in http://server:port/app/dwr/interface/RemoteFunctions.js)


Here are the contents of http://server:port/app/dwr/interface/RemoteFunctions.js

// Provide a default path to dwr.engine
if (dwr == null) var dwr = {};
if (dwr.engine == null) dwr.engine = {};
if (DWREngine == null) var DWREngine = dwr.engine;

if (RemoteFunctions == null) var RemoteFunctions = {};
RemoteFunctions._path = '/dwr4gwt/dwr';
RemoteFunctions.getObject = function(callback) {
dwr.engine._execute(RemoteFunctions._path, 'RemoteFunctions',
'getObject', callback);
}
RemoteFunctions.getBoolean = function(callback) {
dwr.engine._execute(RemoteFunctions._path, 'RemoteFunctions',
'getBoolean', callback);
}
RemoteFunctions.getArray = function(callback) {
dwr.engine._execute(RemoteFunctions._path, 'RemoteFunctions',
'getArray', callback);
}
RemoteFunctions.getNumber = function(callback) {
dwr.engine._execute(RemoteFunctions._path, 'RemoteFunctions',
'getNumber', callback);
}
RemoteFunctions.getString = function(callback) {
dwr.engine._execute(RemoteFunctions._path, 'RemoteFunctions',
'getString', callback);
}
RemoteFunctions.sayHello = function(p0, callback) {
dwr.engine._execute(RemoteFunctions._path, 'RemoteFunctions',
'sayHello', p0, callback);
}


What could be wrong? Any ideas anyone?

eliasb...@gmail.com

unread,
Jan 4, 2008, 6:17:46 PM1/4/08
to Google Web Toolkit
Oh, something I forgot to mention. The problem that occurs is the
variable RemoteFunctions above is reported as inaccessible when called
from within the JSNI function


Here is the generated exception in GWT hosted mode

[ERROR] Uncaught exception escaped
com.google.gwt.core.client.JavaScriptException: JavaScript TypeError
exception: 'RemoteFunctions' is undefined
at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:
481)
at
com.google.gwt.dev.shell.ModuleSpace.invokeNativeVoid(ModuleSpace.java:
270)
at
com.google.gwt.dev.shell.JavaScriptHost.invokeNativeVoid(JavaScriptHost.java:
137)
at
com.eliasbalasis.dwr4gwt.client.DwrAdapter.jsCallRemoteMethod(DwrAdapter.java:
76)
at
com.eliasbalasis.dwr4gwt.client.DwrAdapter.callRemoteMethod(DwrAdapter.java:
57)
at com.eliasbalasis.dwr4gwt.test.client.Test$1.onClick(Test.java:50)
at
com.google.gwt.user.client.ui.ClickListenerCollection.fireClick(ClickListenerCollection.java:
36)
at
com.google.gwt.user.client.ui.FocusWidget.onBrowserEvent(FocusWidget.java:
98)
at com.google.gwt.user.client.DOM.dispatchEventImpl(DOM.java:1265)
at com.google.gwt.user.client.DOM.dispatchEventAndCatch(DOM.java:
1244)
at com.google.gwt.user.client.DOM.dispatchEvent(DOM.java:1211)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
25)


On Jan 5, 12:44 am, "eliasbala...@gmail.com" <eliasbala...@gmail.com>
wrote:
> Hello and happy new year everyone.
>
> I am building a DWR integration library for GWT (http://
> code.google.com/p/dwr4gwt) based on the assumption that I can bind to
> my server-side exposed interfaces whose definitions are available as
> underhttp://server:port/app/dwr/interface/XXX.js(where dwr is
> m...@com.eliasbalasis.dwr4gwt.client.DwrAdapter::fireRemoteMethodCallReply
> (Lcom/google/gwt/core/client/JavaScriptObject;I)
> (
> returnValue,
> remoteMethodCallDescriptorKey
> );
> },
> timeout:
> callTimeout,
> errorHandler:
> function(errorMessage) {
>
> m...@com.eliasbalasis.dwr4gwt.client.DwrAdapter::fireRemoteMethodCallError
> (Ljava/lang/String;I)
> (
> errorMessage,
> remoteMethodCallDescriptorKey
> );
> }
> };
> ...
> ...
> ...
> ...
> ...
> }-*/;
> (where remoteClassName above is the name of the JavaScript object
> defined inhttp://server:port/app/dwr/interface/RemoteFunctions.js)
>
> Here are the contents ofhttp://server:port/app/dwr/interface/RemoteFunctions.js

Robert Hanson

unread,
Jan 4, 2008, 6:54:53 PM1/4/08
to Google-We...@googlegroups.com
Hopefully someone else will provide a longer more detailed reply, but
I believe the problem is that you are not taking into account the fact
that the DWR library is being loaded into a different frame. When GWT
loads it is loaded into an iframe, which is also where your JSNI code
will live.

To access the JS window or document object of the main page you need
to use the JS variables $wnd and $doc. These variables will
automatically be made available to you inside of JSNI blocks.

So when you say...

> (where remoteClassName above is the name of the JavaScript object

You need to reference that JS object using $wnd[remoteClassName] instead.

I hope that helps a little.

Rob
http://roberthanson.name

eliasb...@gmail.com

unread,
Jan 4, 2008, 8:07:52 PM1/4/08
to Google Web Toolkit
Well :-) IT WORKED :-) although I am interested in understanding
clearly what happened.

I must admit I have been careless at the beginning but I now see it
clearly. $wnd refers to main window. so the variables I am looking for
reside there.
I was looking for them inside 'this' which is in another frame.

Many thanks. I can now continue with "teaching" GWT widgets how to
support DWR functionality.
I will soon publish source code/binaries and make a related post. I
will also combine with TIBCO PageBus (OpenAjax Hub impl) from my other
project (http://code.google.com/p/tibcopagebus4gwt) etc etc etc.

Thanks again.

On Jan 5, 1:54 am, "Robert Hanson" <iamroberthan...@gmail.com> wrote:
> Hopefully someone else will provide a longer more detailed reply, but
> I believe the problem is that you are not taking into account the fact
> that the DWR library is being loaded into a different frame. When GWT
> loads it is loaded into an iframe, which is also where your JSNI code
> will live.
>
> To access the JS window or document object of the main page you need
> to use the JS variables $wnd and $doc. These variables will
> automatically be made available to you inside of JSNI blocks.
>
> So when you say...
>
> > (where remoteClassName above is the name of the JavaScript object
>
> You need to reference that JS object using $wnd[remoteClassName] instead.
>
> I hope that helps a little.
>
> Robhttp://roberthanson.name
>
> On Jan 4, 2008 5:44 PM, eliasbala...@gmail.com <eliasbala...@gmail.com> wrote:
>
>
>
> > Hello and happy new year everyone.
>
> > I am building a DWR integration library for GWT (http://
> > code.google.com/p/dwr4gwt) based on the assumption that I can bind to
> > my server-side exposed interfaces whose definitions are available as
> > underhttp://server:port/app/dwr/interface/XXX.js(where dwr is
> > m...@com.eliasbalasis.dwr4gwt.client.DwrAdapter::fireRemoteMethodCallReply
> > (Lcom/google/gwt/core/client/JavaScriptObject;I)
> > (
> > returnValue,
> > remoteMethodCallDescriptorKey
> > );
> > },
> > timeout:
> > callTimeout,
> > errorHandler:
> > function(errorMessage) {
>
> > m...@com.eliasbalasis.dwr4gwt.client.DwrAdapter::fireRemoteMethodCallError
> > (Ljava/lang/String;I)
> > (
> > errorMessage,
> > remoteMethodCallDescriptorKey
> > );
> > }
> > };
> > ...
> > ...
> > ...
> > ...
> > ...
> > }-*/;
> > (where remoteClassName above is the name of the JavaScript object
> > defined inhttp://server:port/app/dwr/interface/RemoteFunctions.js)
>
> > Here are the contents ofhttp://server:port/app/dwr/interface/RemoteFunctions.js

eliasb...@gmail.com

unread,
Jan 4, 2008, 11:43:54 PM1/4/08
to Google Web Toolkit
Well, problem is partially solved. Solution works only within Hosted
Mode browser.
When application is put in a real browser the variables are once again
inaccessible.

Any ideas?


On Jan 5, 3:07 am, "eliasbala...@gmail.com" <eliasbala...@gmail.com>
wrote:
> > > underhttp://server:port/app/dwr/interface/XXX.js(wheredwr is

Robert Hanson

unread,
Jan 5, 2008, 7:54:05 AM1/5/08
to Google-We...@googlegroups.com
If the hosted-mode and web-mode browser are different browsers I would
look for JavaScript incompatibilities. One thing to look for is if
the browser is reporting any JavaScript errors. In IE make sure that
you aren't suppressing them and in FF get a good add-on like FireBug
to help pinpoint the error.

Rob
http://roberthanson.name

eliasb...@gmail.com

unread,
Jan 5, 2008, 8:33:37 AM1/5/08
to Google Web Toolkit
I think I found the cause. I am trying to evaluate (with 'eval') the
remote function call. In the compiled version the variable names are
mangled. In Hosted mode browser the variable names are not mangled.
Therefore the eval expression I am using can only be valid in hosted
mode.

Here is the JSNI function that generates the error followed by a more
details on the exact location of the error


private native void jsCallRemoteMethod(
String remoteClassName,
String remoteMethodName,
JavaScriptObject callParameters,
int remoteMethodCallDescriptorKey,
int callTimeout) /*-{
var me = this;
var remoteClass = $wnd[remoteClassName];
var remoteMethod = remoteClass[remoteMethodName];
var callMetadata = {
callback:
function(returnValue) {
var returnValueShell = {};
var isReturnValueOfPrimitiveType = false;
var isReturnValueOfArrayType = false;
if (typeof(returnValue)=='object') {
returnValueShell = returnValue;
}
if (typeof(returnValue)=='number' ||
typeof(returnValue)=='string' || typeof(returnValue)=='boolean') {
returnValueShell = {value: returnValue};
isReturnValueOfPrimitiveType = true;
}
if (returnValue.constructor.toString().indexOf("Array") != -1) {
isReturnValueOfArrayType = true;
returnValueShell = returnValue;
}

me.@com.eliasbalasis.dwr4gwt.client.DwrAdapter::fireRemoteMethodCallReply(Ljava/
lang/Boolean;Ljava/lang/Boolean;Lcom/google/gwt/core/client/
JavaScriptObject;I)
(
isReturnValueOfPrimitiveType,
isReturnValueOfArrayType,
returnValueShell,
remoteMethodCallDescriptorKey
);
},
timeout:
callTimeout,
errorHandler:
function(errorMessage) {

me.@com.eliasbalasis.dwr4gwt.client.DwrAdapter::fireRemoteMethodCallError(Ljava/
lang/String;I)
(
errorMessage,
remoteMethodCallDescriptorKey
);
}
};
var call = 'remoteMethod(';
var index = 0;
for (var key in callParameters) {
if (index>0)
call += ',';
call += 'callParameters.'+String(key);
index += 1;
}
if (index>0)
call += ',';
call += 'callMetadata)';
eval(call);
}-*/;

the call variable above is finally generated as something like
'remoteMethod(callParameters.param1,callParameters.param2,...,callMetadata)'
where remoteMethod, callParameters, callMetadata etc are JavaScript
variables.
These variables are mangled in the compiled version to something like
a,b,c etc. therefore the 'eval' cannot succeed.
(I extracted this information using Firebug JavaScript debuger on
Firefox browser)

Any ideas?

On Jan 5, 2:54 pm, "Robert Hanson" <iamroberthan...@gmail.com> wrote:
> If the hosted-mode and web-mode browser are different browsers I would
> look for JavaScript incompatibilities. One thing to look for is if
> the browser is reporting any JavaScript errors. In IE make sure that
> you aren't suppressing them and in FF get a good add-on like FireBug
> to help pinpoint the error.
>
> Robhttp://roberthanson.name

Robert Hanson

unread,
Jan 5, 2008, 9:56:08 AM1/5/08
to Google-We...@googlegroups.com
I do see one thing wrong...

> var me = this;
> ...
> me.@com.eliasbalasis.dwr4gwt.client.DwrAdapter::fireRemoteMethodCallReply(...)

The reference to "this" in "var me = this" isn't what you think it is.
It is *not* a reference to the Java class... which means you can't
use it to call back into the Java class.

The "me" in "me.@..." *must* be a Java object that was passed into the
method, like this...

private native void doStuff (DwrAdapter me) {
me.@com.eliasbalasis.dwr4gwt.client.DwrAdapter::fireRemoteMethodCallReply(...)
}

Rob
http://roberthanson.name

eliasb...@gmail.com

unread,
Jan 5, 2008, 10:28:19 AM1/5/08
to Google-We...@googlegroups.com
This works.
Java methods can be referenced and called from JSNI code.
The code you see is from a successfully running example. Same technique I
have used in another project that is successfully running in both hosted
mode and all supported browsers.

The Java class is 'called back'. I am using 'me' because 'this' has a
different value where I am using it, so I am using 'me' (original 'this')
instead.

The real problem is the variable name mangling :-(
When translated to JavaScript by the GWT compiler my variables no longer
have the same name and as a result my 'eval' expressions fail :-(

I thought of re-writing the 'eval' expressions and put actual values in
them, but then I wouldn't have a way of expressing the Java method signature
in the 'eval' expression, since whatever passed in 'eval' is not translated
and it wouldn't work in the browser case where everything needs to be
mangled.

Any ideas?

George Georgovassilis

unread,
Jan 5, 2008, 1:17:32 PM1/5/08
to Google Web Toolkit
Hello Elias

JSNI lives in the twilight zone between Java (GWT) and Javascript; the
GWT compiler shortens method names in order to preserve space and
makes sure all references from JSNI are renamed as well. Have a look
at Ray Cromwell's export library described in this blog [1] where he
tackles exactly this problem:making methods in an GWT component
available to external javascript libraries.

[1] http://timepedia.blogspot.com/2007/06/gwt-demystified-generators-part-deux.html
> m...@com.eliasbalasis.dwr4gwt.client.DwrAdapter::fireRemoteMethodCallReply(..
> .)
>
> The reference to "this" in "var me = this" isn't what you think it is.
> It is *not* a reference to the Java class... which means you can't
> use it to call back into the Java class.
>
> The "me" in "me.@..." *must* be a Java object that was passed into the
> method, like this...
>
> private native void doStuff (DwrAdapter me) {
>
> m...@com.eliasbalasis.dwr4gwt.client.DwrAdapter::fireRemoteMethodCallReply(..
> .)
> }
>
> Robhttp://roberthanson.name
>
> On Jan 5, 2008 8:33 AM, eliasbala...@gmail.com <eliasbala...@gmail.com>
> m...@com.eliasbalasis.dwr4gwt.client.DwrAdapter::fireRemoteMethodCallReply(Lj
> ava/
> > lang/Boolean;Ljava/lang/Boolean;Lcom/google/gwt/core/client/
> > JavaScriptObject;I)
> > (
> > isReturnValueOfPrimitiveType,
> > isReturnValueOfArrayType,
> > returnValueShell,
> > remoteMethodCallDescriptorKey
> > );
> > },
> > timeout:
> > callTimeout,
> > errorHandler:
> > function(errorMessage) {
>
> m...@com.eliasbalasis.dwr4gwt.client.DwrAdapter::fireRemoteMethodCallError(Lj
> ...
>
> read more »

eliasb...@gmail.com

unread,
Jan 5, 2008, 1:31:57 PM1/5/08
to Google Web Toolkit
Indeed, this is twilight zone. I have read most of Ray Cromwell's
'demystified' series and loved it.

But my problem is not how to call Java methods from JSNI (this job is
simplified gwt-exporter)
The problem is how to 'eval' in JSNI something that is not known in
advance.
To 'eval' a javascript expression that is programmatically(JSNI)
discovered.

There I discovered that the JSNI variables are mangled at compile time
making my expressions invalid, since the variables I was expecting had
different names.
I also discovered that the mangled variable names do not change unless
the variables in the JSNI function change.

Based on this assumption I was able to create a solution.
But I worry that is perhaps not stable behavior and in the future will
perhaps change. :-(

Here are some of my related posts

http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/59cb58eee7ff7941

http://groups.google.com/group/Google-Web-Toolkit-Contributors/browse_thread/thread/aca8313a61d54c16


On Jan 5, 8:17 pm, George Georgovassilis <g.georgovassi...@gmail.com>
wrote:
> Hello Elias
>
> JSNI lives in the twilight zone between Java (GWT) and Javascript; the
> GWT compiler shortens method names in order to preserve space and
> makes sure all references from JSNI are renamed as well. Have a look
> at Ray Cromwell's export library described in this blog [1] where he
> tackles exactly this problem:making methods in an GWT component
> available to external javascript libraries.
>
> [1]http://timepedia.blogspot.com/2007/06/gwt-demystified-generators-part...
> ...
>
> read more »

eliasb...@gmail.com

unread,
Jan 5, 2008, 1:34:50 PM1/5/08
to Google Web Toolkit
Here is also a reference to the project requiring the above mentioned
functionality
http://code.google.com/p/dwr4gwt


On Jan 5, 8:31 pm, "eliasbala...@gmail.com" <eliasbala...@gmail.com>
wrote:
> Indeed, this is twilight zone. I have read most of Ray Cromwell's
> 'demystified' series and loved it.
>
> But my problem is not how to call Java methods from JSNI (this job is
> simplified gwt-exporter)
> The problem is how to 'eval' in JSNI something that is not known in
> advance.
> To 'eval' a javascript expression that is programmatically(JSNI)
> discovered.
>
> There I discovered that the JSNI variables are mangled at compile time
> making my expressions invalid, since the variables I was expecting had
> different names.
> I also discovered that the mangled variable names do not change unless
> the variables in the JSNI function change.
>
> Based on this assumption I was able to create a solution.
> But I worry that is perhaps not stable behavior and in the future will
> perhaps change. :-(
>
> Here are some of my related posts
>
> http://groups.google.com/group/Google-Web-Toolkit/browse_thread/threa...
>
> http://groups.google.com/group/Google-Web-Toolkit-Contributors/browse...
> ...
>
> read more »

George Georgovassilis

unread,
Jan 5, 2008, 1:39:40 PM1/5/08
to Google-We...@googlegroups.com
Correct me if I am wrong, but I thought the exporter made sure that annotated methods received a fixed, immutable name. Although it might be possible after all that variable names change, essentially all you need is just one object (let's say a session object that acts as a container for all variables you may need) - then you can access all methods there under their exported name. Or am I missing something?

> > > (returnValue.constructor.toString ().indexOf("Array") != -1) {
> ...
>
> read more »

eliasb...@gmail.com

unread,
Jan 5, 2008, 1:53:38 PM1/5/08
to Google Web Toolkit
Hmmmmm sounds related.
But I wouldn't want, at least not at this point (too difficult and
time consuming), to implement a custom generator that would manage my
javascript variable names and ensure that javascript references inside
'eval' statements are replaced by their mangled equivalents.

On the other hand it wouldn't work in hosted mode since no mangling
occurs there.

So unless I can somehow predict the mangled variable name I can't make
it fully functional.

I observed that mangled names do not change unless the code in the
related JSNI function changes.
Based on this assumption I was able to create a solution (checking for
non-mangled name and if 'undefined' then use the mangled version) that
works both in hosted and web mode.

I hope this behavior does not change in the future :-)


On Jan 5, 8:39 pm, "George Georgovassilis"
<g.georgovassi...@gmail.com> wrote:
> Correct me if I am wrong, but I thought the exporter made sure that
> annotated methods received a fixed, immutable name. Although it might be
> possible after all that variable names change, essentially all you need is
> just one object (let's say a session object that acts as a container for all
> variables you may need) - then you can access all methods there under their
> exported name. Or am I missing something?
>
> On Jan 5, 2008 7:34 PM, eliasbala...@gmail.com <eliasbala...@gmail.com>
> > > > > (returnValue.constructor.toString().indexOf("Array") != -1) {
> ...
>
> read more »
Reply all
Reply to author
Forward
0 new messages