ant-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Jacob Beard <jbea...@cs.mcgill.ca>
Subject Re: Rhino global.load() in script context
Date Sat, 21 Aug 2010 22:44:40 GMT
Hi Greg,

Thanks a lot for this! This does exactly what I want.

I had actually just about given up, as I realized that the load function 
I was attempting to define would have the shortcoming of essentially 
capturing any local variables eval'ed within it. This mean that while 
dojo worked because it was declared in the global scope, RequireJS would 
not load because its top-level argument ("require") was declared using var.

I'm mentioning this now only because it's amusing, but to work around 
this, I tried imagining a way to exit the load function to eval the 
string to be loaded, thus allowing local variables declared within the 
string to be declared in the global scope; then returning from the 
global scope to the call site of the load function. The only way I could 
think to do this was with continuations. Converting to the 
continuation-passing style was not an option, because passing in a 
callback to load would break the API. Fortunately, Rhino exposes a 
native Continuation. After some playing around, I found that this code 
had the desired effect:

/*

     this file is to test a technique for creating a load function in Rhino

*/

(function(){

     myLoadLocal = function(str){

         eval(str);

     }

     function call_with_current_continuation() {

         var kont = new Continuation();

         return kont;

     }

     var evalString = null, afterEval = null;

     var beforeEval = call_with_current_continuation();

     if(evalString){

         eval(evalString);

         evalString=null;

         afterEval(null);

     }

     myLoadContinuation = function(str){

         evalString = str;

         afterEval = call_with_current_continuation();

         if(afterEval instanceof Continuation){

             beforeEval(beforeEval);

         }else{

             return;

         }

     }

     myLoadLocal("var foo=1;");

     print(typeof foo);    //should be undefined

     myLoadContinuation("var bar=2;");

     print(typeof bar);    //should be number

     print(bar);        //should be 2

     //see if it works again

     myLoadContinuation("var bat=3;");

     print(typeof bat);    //should be number

     print(bat);        //should be 3

})()


I think there's probably a more elegant way to use continuations to do 
this, but this was the first thing I got working. One caveat to this, 
however, is that Continuations in Rhino only work when run in 
interpreted mode, without optimizations (-opt -1). Otherwise it fails 
with the following error:

js: Direct call is not supported

When I brought this back into the Ant script context, it failed with 
this error as well, so it appears that this technique would not work in 
Ant for this reason.


I wonder if its worth discussing whether removing the global functions 
normally found in Rhino is a desirable behaviour for Ant. Other 
scripting languages include facilities for importing code as part of 
their core syntax (e.g. Jython's import statement), so this cannot be 
easily removed for them, but for Rhino, the load function is simply part 
of the global object, and can be easily removed from the embedding 
context. But I'm not sure if this is actually a good thing to do. 
Certainly it reduces the utility of the Ant script context, and 
increases its verbosity for situations where external scripts must be 
loaded via a module loader, such as Dojo or RequireJS. Do you think this 
is something that would be worth bringing up on the developer's list? 
Would it be useful to file a bug report or feature request?

Let me know what you think. Thanks,

Jake


On 10-08-21 05:37 PM, Greg Roodt wrote:
> This might work for you:
>
>      <project default="hello" name="helloworld" basedir=".">
>         <target name="hello">
>             <script language="javascript" manager="bsf">
>             <classpath>
>                 <fileset dir="rhino-lib" includes="*.jar"></fileset>
>             </classpath><![CDATA[
>             importPackage(java.lang, java.util, java.io);
>             System.out.println("Hello from JavaScript!!");
>             //create shell, execute something and grab global
>             var shell = org.mozilla.javascript.tools.shell.Main;
>             var args = ["-e","var a='STRING';"];
>              shell.exec(args);
>             var shellGlobal = shell.global;
>
>             //grab functions from shell global and place in current global
>             var load=shellGlobal.load;
>             var print=shellGlobal.print;
>             var defineClass=shellGlobal.defineClass;
>             var deserialize=shellGlobal.deserialize;
>             var doctest=shellGlobal.doctest;
>             var gc=shellGlobal.gc;
>             var help=shellGlobal.help;
>             var loadClass=shellGlobal.loadClass;
>             var quit=shellGlobal.quit;
>             var readFile=shellGlobal.readFile;
>             var readUrl=shellGlobal.readUrl;
>             var runCommand=shellGlobal.runCommand;
>             var seal=shellGlobal.seal;
>             var serialize=shellGlobal.serialize;
>             var spawn=shellGlobal.spawn;
>             var sync=shellGlobal.sync;
>             var toint32=shellGlobal.toint32;
>             var version=shellGlobal.version;
>             var environment=shellGlobal.environment;
>
>             //test your bad self
>             load("test.js");
>
>             ]]></script>
>         </target>
>      </project>
>
> test.js:
> var a = function() {
> print("test");
> help();
> var scriptContents = readFile("test.js");
> print(scriptContents);
> var ver = version();
> print("version:"+ver);
> print(this);
> for(var prop in this){
> print(prop);
> }
> }
> a();
>
>
>
> On Sat, Aug 21, 2010 at 7:03 PM, Jacob Beard<jbeard4@cs.mcgill.ca>  wrote:
>
>    
>> Hi Greg,
>>
>> Thanks for your response. Replies below:
>>
>>
>> On 10-08-21 01:41 PM, Greg Roodt wrote:
>>
>>      
>>> I believe load() is part of Rhino Shell. I think all that the<script />
>>> task runs when using JavaScript is the interpreter. It would only have the
>>> pure Javascript standard language features (and a few bits and pieces to
>>> interact with Java and the execution context).
>>>
>>>
>>>        
>> load() is normally exposed as part of the global object when running Rhino,
>> in the shell or the interpreter. All the js module loaders that support
>> Rhino that I've encountered, including RequireJS and dojo, make use of
>> load() to load JavaScript modules.
>>
>>   It might be easier to run the shell for each test? Like so:
>>      
>>> java org.mozilla.javascript.tools.shell.Main [options]
>>> script-filename-or-url [script-arguments]
>>> https://developer.mozilla.org/en/Rhino_Shell#Invoking_the_Shell
>>>
>>> Or like John Resig does with env.js:
>>> http://ejohn.org/blog/bringing-the-browser-to-the-server/
>>>
>>>
>>>        
>> I'm using that technique for other parts of my code, but it would be much
>> easier to simply hook into Ant's ResourceSet data structures for this part,
>> as it's possible to register a number of unit tests with dojo before running
>> them.
>>
>>
>>   Or maybe, define your own global load() function inside the<script />
>>      
>>>   tag?
>>>
>>>
>>>        
>> That's what I'm working on. This seems to work, but I still need to test it
>> with the dojo module loader:
>>
>>         <script language="javascript" manager="bsf">
>>
>>             <classpath>
>>
>>                 <fileset dir="../../../lib/java/" includes="js.jar"/>
>>
>>                 <fileset dir="../../../lib/build-java/"
>> includes="*.jar"></fileset>
>>
>>             </classpath><![CDATA[
>>
>>             //define load in global scope
>>
>>             function readFile(path){
>>
>>                 stream = new java.io.FileInputStream(new
>> java.io.File(path));
>>
>>                 fc = stream.getChannel();
>>
>>                 bb = fc.map(java.nio.channels.FileChannel.MapMode.READ_ONLY,
>> 0, fc.size());
>>
>>                 return
>> java.nio.charset.Charset.defaultCharset().decode(bb).toString();
>>
>>             }
>>
>>             load = function(path){
>>
>>                 eval(String(readFile(path)))
>>
>>             }
>>
>>             echo = helloworld.createTask("echo");
>>
>>             var contents = readFile('hello.js')
>>
>>             echo.setMessage(contents);
>>
>>             echo.perform();
>>
>>             load('hello.js')
>>
>>             echo.perform();
>>
>>         ]]></script>
>>
>> hello.js:
>>
>> echo.setMessage("hello world!");
>>
>>
>> Outputs:
>>
>> hello:
>>
>>      [echo] echo.setMessage("hello world!");
>>
>>      [echo] hello world!
>>
>>
>>
>> Thanks,
>>
>> Jake
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: user-unsubscribe@ant.apache.org
>> For additional commands, e-mail: user-help@ant.apache.org
>>
>>
>>      
>    

---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscribe@ant.apache.org
For additional commands, e-mail: user-help@ant.apache.org


Mime
View raw message