JavaScript variable name mangling

10 views
Skip to first unread message

eliasb...@gmail.com

unread,
Jan 5, 2008, 8:54:30 AM1/5/08
to Google Web Toolkit Contributors
Trying to programmatically construct a JavaScript call in JSNI
function using 'eval' function
Problem is that the names of JavaScript variables I declare in JSNI
function and pass to 'eval' call are mangled in the compiled version
of the function making thus the 'eval' expression invalid generating a
related error.


Is there a way to either know the mangled name in advance (before GWT
compilation) or deactivate JavaScript variable names mangling for
desired JSNI functions?

eliasb...@gmail.com

unread,
Jan 5, 2008, 1:10:23 PM1/5/08
to Google Web Toolkit Contributors
I observed that if the variables in a JSNI function do not change the
mangling does not change either.
The problem was solved with code based on this exact assumption.
Can I assume this as a stable behaviour that will not change in the
future?

On Jan 5, 3:54 pm, "eliasbala...@gmail.com" <eliasbala...@gmail.com>
wrote:

Ray Cromwell

unread,
Jan 5, 2008, 7:43:48 PM1/5/08
to Google-Web-Tool...@googlegroups.com
I would not depend on these being stable. And what are you
programmatically calling with eval? Is the eval really needed? Perhaps
you should post your code or explain the use case more in depth.

-Ray

eliasb...@gmail.com

unread,
Jan 6, 2008, 9:56:49 AM1/6/08
to Google Web Toolkit Contributors
I am using 'eval' in JSNI function to make a call to a javascript
method that is programmatically discovered (name and parameters are
passed as arguments to JSNI function)
Here is a related post containing more details and a real code
snapshot.

http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/7bb922e7a6644b24/916ab3f55a239860?lnk=gst&q=mangling#916ab3f55a239860


On Jan 6, 2:43 am, "Ray Cromwell" <cromwell...@gmail.com> wrote:
> I would not depend on these being stable. And what are you
> programmatically calling with eval? Is the eval really needed? Perhaps
> you should post your code or explain the use case more in depth.
>
> -Ray
>

eliasb...@gmail.com

unread,
Jan 6, 2008, 10:01:52 AM1/6/08
to Google Web Toolkit Contributors
On second thought, for your convenience, here is the code sample

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;
}

m...@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) {

m...@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);
}-*/;



remoteClassName, remoteMethodName, callParameters define which
javascript method must be called resulting in call variable in the end
having a value like
'remoteMethod[callParameters.param1,]callParameters.param2,callParameters.paramN,callMetadata)'
but remoteClassName, remoteMethodName, callParameters are mangled so
here is a better version that used either the mangled names or the
original ones depending on what is available (implying being in hosted
mode or not). but it works only if the mangled names do not change.
Currently they don't unless the JSNI code is changed which is
acceptable.

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

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

me.@com.eliasbalasis.dwr4gwt.client.DwrAdapter::fireRemoteMethodCallError(Ljava/
lang/String;I)
(
errorMessage,
remoteMethodCallDescriptorKey
);
}
};

// use hosted mode variable names
// assuming we are in hosted mode
var remoteMethodExpression = 'remoteMethod';
var callParametersExpression = 'callParameters';
var callMetadataExpression = 'callMetadata';
var isWebMode = false;
try {
eval('remoteMethod');
} catch(ex) {
var isWebMode = true;
}
if (isWebMode) {
// we are not in hosted mode
// use mangled/translated variable names
// we are in web mode
remoteMethodExpression = 'q';
callParametersExpression = 'h';
callMetadataExpression = 'f';
}
var call = remoteMethodExpression+'(';
var index = 0;
for (var key in callParameters) {
if (index>0)
call += ',';
call += callParametersExpression+'.'+String(key); //
index += 1;
}
if (index>0)
call += ',';
call += callMetadataExpression+')'; //
eval(call);
}-*/;


On Jan 6, 4:56 pm, "eliasbala...@gmail.com" <eliasbala...@gmail.com>
wrote:
> I am using 'eval' in JSNI function to make a call to a javascript
> method that is programmatically discovered (name and parameters are
> passed as arguments to JSNI function)
> Here is a related post containing more details and a real code
> snapshot.
>
> http://groups.google.com/group/Google-Web-Toolkit/browse_thread/threa...

Ray Cromwell

unread,
Jan 6, 2008, 1:32:04 PM1/6/08
to Google-Web-Tool...@googlegroups.com
Did try using Function.apply() instead of eval()? What's preventing
simply copying the call params to an Array and using apply()?

Alternatively, offhand, you could look at maybe using a curried
closure to capture the variables and eliminate their reference in the
eval expression.

-Ray

eliasb...@gmail.com

unread,
Jan 6, 2008, 2:01:49 PM1/6/08
to Google Web Toolkit Contributors
Well, my JavaScript knowledge is definitely not advanced :-) I didn't
know such a thing existed (I was hoping :-) it did though)
apply() is a perfect match for my case. (perhaps a combination of
closure and apply() would work even better)
I will try it.
Many thanks.

On Jan 6, 8:32 pm, "Ray Cromwell" <cromwell...@gmail.com> wrote:
> Did try using Function.apply() instead of eval()? What's preventing
> simply copying the call params to an Array and using apply()?
>
> Alternatively, offhand, you could look at maybe using a curried
> closure to capture the variables and eliminate their reference in the
> eval expression.
>
> -Ray
>
> > m...@com.eliasbalasis.dwr4gwt.client.DwrAdapter::fireRemoteMethodCallReply(Lcom/
> > google/gwt/core/client/JavaScriptObject;IZZ)
> > (
> > returnValueShell,
> > remoteMethodCallDescriptorKey,
> > isReturnValueOfPrimitiveType,
> > isReturnValueOfArrayType
> > );
> > },
> > timeout:
> > callTimeout,
> > errorHandler:
> > function(errorMessage) {
>
> > m...@com.eliasbalasis.dwr4gwt.client.DwrAdapter::fireRemoteMethodCallError(Ljava/
> > lang/String;I)
> > (
> > errorMessage,
> > remoteMethodCallDescriptorKey
> > );
> > }
> > };
>
> ...
>
> read more »

eliasb...@gmail.com

unread,
Jan 6, 2008, 2:17:51 PM1/6/08
to Google Web Toolkit Contributors
It worked. No closure used finally. apply() was enough.
Thanks again.

On Jan 6, 9:01 pm, "eliasbala...@gmail.com" <eliasbala...@gmail.com>
wrote:
> ...
>
> read more »

Reinier Zwitserloot

unread,
Jan 6, 2008, 5:30:04 PM1/6/08
to Google Web Toolkit Contributors
For future reference, if you must do it, you can stick java-GWT
methods into your window namespace with predefined names by using JSNI
to assign functions around. Try something like this:

package test.package;
import whateverIsNeeded;

public class MyProjectEntryPoint extends EntryPoint {
public void onModuleLoad() {
makeJavaMethodsAvailable();
//other stuff
}

private static native void makeJavaMethodsAvailable() /*-{
$wnd.foobar = function(someValue) {
@test.package.MyProjectEntryPoint::foobar(I)(someValue);
};
}-*/;

public static int foobar(int whatever) {
return whatever * 2;
}
}

now, *AFTER* your module entry point runs, any javascript code can do
this:

alert(foobar(5)); and you'll see a popup with '10' in it.

On Jan 6, 8:17 pm, "eliasbala...@gmail.com" <eliasbala...@gmail.com>
wrote:
> > > >                                 // we are in web mode...
>
> read more »

Ray Cromwell

unread,
Jan 6, 2008, 7:08:11 PM1/6/08
to Google-Web-Tool...@googlegroups.com
His issue wasn't exporting Java methods via JS bridge methods (ala GWT
Exporter), but simply evaling JSNI parameters:

e.g.
public native void foo(int a, String b) /*-
eval("a + b"); // boom!, this doesn't work
-*/;

Basically, he was doing dynamic invocation of a function by name, and
variables passed in a JSO without using Function.apply(), but rather
constructing the callsite as a string and eval()ing it, but GWT
obfuscation affects parameters as well.


-Ray

On Jan 6, 2008 2:30 PM, Reinier Zwitserloot <rein...@gmail.com> wrote:
>
> For future reference, if you must do it, you can stick java-GWT
> methods into your window namespace with predefined names by using JSNI
> to assign functions around. Try something like this:
>
> package test.package;
> import whateverIsNeeded;
>
> public class MyProjectEntryPoint extends EntryPoint {
> public void onModuleLoad() {
> makeJavaMethodsAvailable();
> //other stuff
> }
>
> private static native void makeJavaMethodsAvailable() /*-{
> $wnd.foobar = function(someValue) {
> @test.package.MyProjectEntryPoint::foobar(I)(someValue);
> };
> }-*/;
>
> public static int foobar(int whatever) {
> return whatever * 2;
> }
> }
>
> now, *AFTER* your module entry point runs, any javascript code can do
> this:
>
> alert(foobar(5)); and you'll see a popup with '10' in it.
>

> On Jan 6, 8:17pm, "eliasbala...@gmail.com" <eliasbala...@gmail.com>

eliasb...@gmail.com

unread,
Jan 6, 2008, 7:14:07 PM1/6/08
to Google-Web-Tool...@googlegroups.com
Oh yes, that's very useful. I have used it, not to stick java-GWT
methods into the window namespace but to handle JavaScript callbacks as
java-GWT functions. my tibcopagebus4gwt (
http://code.google.com/p/tibcopagebus4gwt/ ) and dwr4gwt (
http://code.google.com/p/dwr4gwt/ ) libraries use this technique
extensively)

Thanks.

eliasb...@gmail.com

unread,
Jan 6, 2008, 7:17:08 PM1/6/08
to Google Web Toolkit Contributors
Yes, the problem was of a different nature. It is solved now thanks to
Ray Cromwell's help.
Thank you guys, I couldn't have done it without your help ;-)


On Jan 7, 2:14 am, <eliasbala...@gmail.com> wrote:
> Oh yes, that's very useful. I have used it, not to stick java-GWT
> methods into the window namespace but to handle JavaScript callbacks as
> java-GWT functions. my tibcopagebus4gwt (http://code.google.com/p/tibcopagebus4gwt/) and dwr4gwt (http://code.google.com/p/dwr4gwt/) libraries use this technique
> ...
>
> read more »

Scott Blum

unread,
Jan 7, 2008, 12:31:22 PM1/7/08
to Google-Web-Tool...@googlegroups.com
On Jan 6, 2008 7:08 PM, Ray Cromwell <cromw...@gmail.com> wrote:
His issue wasn't exporting Java methods via JS bridge methods (ala GWT
Exporter), but simply evaling  JSNI parameters:

e.g.
public native void foo(int a, String b) /*-
  eval("a + b"); // boom!, this doesn't work
-*/;

I've been doing some thinking about this case, where a user would like to create an eval environment and cannot do so due to obfuscation.  I've thought of three possible solutions:

1: Annotation to prevent obfuscation of params/locals.

@NoObfuscateLocals

public native void foo(int a, String b) /*-
  eval("a + b"); // boom!, this doesn't work
-*/;

Seems workable if slightly clunky.

2: Compiler detects eval calls.


public native void foo(int a, String b) /*-
  eval("a + b"); // Compiler treats the eval call magically and does not obfuscate locals
-*/;

I'm not particular fond of this, it seems kind of error prone.

3: User workaround, eval your eval environment.


public native void foo(int a, String b) /*-
  var unobfuscatedFunc = eval("function(a,b) { eval(\"a + b\"); }");
  unobfuscatedFunc(a, b);
-*/;

This is just plain nasty.


Any other ideas?

Scott

eliasb...@gmail.com

unread,
Jan 7, 2008, 4:01:54 PM1/7/08
to Google-Web-Tool...@googlegroups.com

I agree. Solution 3 is better but depending on the nature/complexity of the ‘eval’ expression it is perhaps not good enough for all cases.

 

I was thinking also about a custom generator that will (at compile time) check for ‘eval’ calls and prefer using the non-mangled version of variable names for ‘eval’ calls.

Any comments?

 


From: Google-Web-Tool...@googlegroups.com [mailto:Google-Web-Tool...@googlegroups.com] On Behalf Of Scott Blum
Sent: Monday, January 07, 2008 7:31 PM
To: Google-Web-Tool...@googlegroups.com
Subject: [gwt-contrib] Re: JavaScript variable name mangling

 

On Jan 6, 2008 7:08 PM, Ray Cromwell <cromw...@gmail.com> wrote:

Ray Cromwell

unread,
Jan 7, 2008, 5:02:46 PM1/7/08
to Google-Web-Tool...@googlegroups.com
IIRC, A generator can't really check for the eval() calls because
generators don't have access to the complete AST. I actually think
eval() is not really needed, most of the cases I can think of to use
eval() can be done with other techniques, and eval() is kinda slow
too. I benchmarked it one time, and it was orders of magnitude slower
than a normal function invocation.

-Ray

eliasb...@gmail.com

unread,
Jan 7, 2008, 5:23:31 PM1/7/08
to Google Web Toolkit Contributors
I can't but agree on this.

I also believe that all 'eval' cases, regardless of their nature/
complexity, fall into some pattern that can be represented with
alternative ways like func.apply() (just like I did for my case)

So I finally believe that func.apply() (or similar) is best solution,
plus it has higher performance as you already mentioned.

On Jan 8, 12:02 am, "Ray Cromwell" <cromwell...@gmail.com> wrote:
> IIRC, A generator can't really check for the eval() calls because
> generators don't have access to the complete AST. I actually think
> eval() is not really needed, most of the cases I can think of to use
> eval() can be done with other techniques, and eval() is kinda slow
> too. I benchmarked it one time, and it was orders of magnitude slower
> than a normal function invocation.
>
> -Ray
>
> On Jan 7, 2008 1:01 PM, <eliasbala...@gmail.com> wrote:
>
>
>
> > I agree. Solution 3 is better but depending on the nature/complexity of the
> > 'eval' expression it is perhaps not good enough for all cases.
>
> > I was thinking also about a custom generator that will (at compile time)
> > check for 'eval' calls and prefer using the non-mangled version of variable
> > names for 'eval' calls.
>
> > Any comments?
>
> > ________________________________
>
> > From: Google-Web-Tool...@googlegroups.com
> > [mailto:Google-Web-Tool...@googlegroups.com] On Behalf Of Scott
> > Blum
> > Sent: Monday, January 07, 2008 7:31 PM
> > To: Google-Web-Tool...@googlegroups.com
>
> > Subject: [gwt-contrib] Re: JavaScript variablenamemangling
>

Scott Blum

unread,
Jan 7, 2008, 5:52:45 PM1/7/08
to Google-Web-Tool...@googlegroups.com
Actually, RPC could use this feature for deserialization, in theory.  Imagine that the client and server can agree upon a mapping between class types and construction/deserialization function calls.  Such that:

com.example.Foo => a(int,)
com.example.Bar => b(3 args)
etc..

Then you could generate an eval environment like this..

JavaScriptObject createDeserializationWhammy() /*-{
  var a = @com.example.Foo_TypeSerializer::deserialize (...);
  var b = @com.example.Bar_TypeSerializer::deserialize(...);
  return new function(stringToEval) {
    return eval(stringToEval);
  };
}-*/;

eliasb...@gmail.com

unread,
Jan 7, 2008, 5:57:26 PM1/7/08
to Google Web Toolkit Contributors
I believe a quite related approach is followed by gwt-jsonizer
I am using it for serialization.deserialization between Java and
JavaScript objects.
I checked the source code to introduce some required fixes and I
observer the technique you describe.

Have a look at http://code.google.com/p/gwt-jsonizer/


On Jan 8, 12:52 am, "Scott Blum" <sco...@google.com> wrote:
> Actually, RPC could use this feature for deserialization, in theory.
> Imagine that the client and server can agree upon a mapping between class
> types and construction/deserialization function calls. Such that:
>
> com.example.Foo => a(int,)
> com.example.Bar => b(3 args)
> etc..
>
> Then you could generate an eval environment like this..
>
> JavaScriptObject createDeserializationWhammy() /*-{
> var a = @com.example.Foo_TypeSerializer::deserialize(...);
> var b = @com.example.Bar_TypeSerializer::deserialize(...);
> return new function(stringToEval) {
> return eval(stringToEval);
> };
>
> }-*/;
>
Reply all
Reply to author
Forward
0 new messages