Translate

Archives

Updating the Fedora 14 JavaScript Shell

For those who are not familiar with a JavaScript shell, it is a command line interface to a JavaScript engine. Similar to Python or Ruby, the JavaScript shell has two modes of operation. You can use it as an interactive shell, in which you type JavaScript code at a prompt and get instant results, or you can invoke a JavaScript program.

The easiest way that I know of to build a JavaScript shell on Fedora is to download and build either the SpiderMonkey or TraceMonkey JavaScript engine, both of which come with a JavaScript Shell. TraceMonkey recently replaced SpiderMonkey in the Firefox browser. TraceMonkey added native‐code compilation to SpiderMonkey based on a technique developed at UC Irvine called “trace trees”, and building on code and ideas shared with the Tamarin project. The result was a significant speed increase both in the browser chrome and page content.

Another JavaScript engine is Rhino. However, Rhino is written using the Java language whereas SpiderMonkey and TraceMonkey are pure C language implementations which conforms to ECMA-262 Edition 3. Source code for all three JavaScript engines is readily available on the Mozilla website.

Fedora 14 comes with the js-1..70-12 package.

# rpm -ql js
/usr/bin/js
/usr/lib64/libjs.so.1
/usr/share/doc/js-1.70
/usr/share/doc/js-1.70/README.html

This is an old version (1.7) of the SpiderMonkey engine which was released in October 2007. SpiderMonkey 1.8 was released in March 2009 but evidently the package was not updated to reflect the new release nor was it updated when TraceMonkey replaced SpiderMonkey as the Firefox JavaScript engine in June 2009.

This post describes how to build the latest version of TraceMonkey on Fedora 14.

To build TraceMonkey the GNU Compiler C++ toolchain must be installed. In addition, you require autoconf 2.13 and the Mercurial SCM (Source Control Management) tool.

The following Bash shell script can be used to build TraceMonkey on Fedora 14. It assumes that the GCC C++ toolchain is already installed.

#!/bin/bash

# customize
BUILDTIP=/var/tmp

if (( $(id -u) != 0 ))
then
   echo "You must be root to execute this script"
   exit 1
fi

# check mercurial is installed. Install if necessary
yum list installed mercurial > /dev/null
[[ $? == 1 ]] && yum install mercurial

# setup build area
cd ${BUILDTIP} || exit 1
[[ -d tracemonkey ]] && rm -rf tracemonkey
[[ -d autoconf-2.13 ]] && rm -rf autoconf-2.13
# Mozilla builds require autoconf-2.13

wget 'http://ftp.gnu.org/gnu/autoconf/autoconf-2.13.tar.gz'
tar xvf autoconf-2.13.tar.gz
rm autoconf-2.13.tar.gz
cd autoconf-2.13
./configure
# installs /usr/local
make install

# get TraceMonkey sources using Mercurial
cd ${BUILDTIP} 
hg clone http://hg.mozilla.org/tracemonkey/

# build TraceMonkey
cd ${BUILDTIP}/tracemonkey/js/src || exit 1
/usr/local/bin/autoconf
./configure
make

# remove autoconf-2.13
cd ${BUILDTIP}/autoconf-2.13 || exit 1
make uninstall
cd ${BUILDTIP}
rm -rf autoconf-2.13

exit 0


Note that it builds TraceMonkey in /var/tmp/tracemonkey. Change the BUILDTIP variable if you would prefer to build TraceMonkey somewhere else. You also need to be root because packages are installed. Not a good practice except on your own development platforms! Also note that the contents of /var/tmp may be deleted upon system reboot.

The above script should build TraceMonkey without encountering any errors. The TraceMonkey JavaScript shell should then be available to you at /var/tmp/tracemonkey/src/js/shell/js.

Now that you have a JavaScript shell, how do you use it and what can you do with it?

$ ./js
js> help
function help() {[native code]}
js> help()
JavaScript-C 1.8.0 pre-release 1 2007-10-03
Command Description
======= ===========
version([number]) Get or force a script compilation version number
revertVersion() Revert previously set version number
options([option …]) Get or toggle JavaScript options
load([‘foo.js’ …]) Load files named by string arguments
readline() Read a single line from stdin
print([exp …]) Evaluate and print expressions
putstr([exp]) Evaluate and print expression without newline
dateNow() Return the current time with sub-ms precision
help([name …]) Display usage and help messages
quit() Quit the shell
assertEq(actual, expected[, msg])
Throw if the first two arguments are not the same (both +0 or both -0,
both NaN, or non-zero and ===)
assertJit() Throw if the calling function failed to JIT
gc() Run the garbage collector
gcparam(name, value)
Wrapper for JS_SetGCParameter. The name must be either ‘maxBytes’ or
‘maxMallocBytes’ and the value must be convertable to a positive uint32
countHeap([start[, kind]])
Count the number of live GC things in the heap or things reachable from
start when it is given and is not null. kind is either ‘all’ (default) to
count all things or one of ‘object’, ‘double’, ‘string’, ‘function’,
‘qname’, ‘namespace’, ‘xml’ to count only things of that kind
makeFinalizeObserver()
get a special object whose finalization increases the counter returned
by the finalizeCount function
finalizeCount()
return the current value of the finalization counter that is incremented
each time an object returned by the makeFinalizeObserver is finalized
setDebug(debug) Set debug mode
setDebuggerHandler(f) Set handler for debugger keyword to f
setThrowHook(f) Set throw hook to f
trap([fun, [pc,]] exp) Trap bytecode execution
untrap(fun[, pc]) Remove a trap
line2pc([fun,] line) Map line number to PC
pc2line(fun[, pc]) Map PC to line number
stackQuota([number]) Query/set script stack quota
stringsAreUTF8() Check if strings are UTF-8 encoded
testUTF8(mode) Perform UTF-8 tests (modes are 1 to 4)
throwError() Throw an error from JS_ReportError
build() Show build date and time
clear([obj]) Clear properties of object
intern(str) Internalize str in the atom table
clone(fun[, scope]) Clone function object
getpda(obj) Get the property descriptors for obj
getslx(obj) Get script line extent
toint32(n) Testing hook for JS_ValueToInt32
evalcx(s[, o])
Evaluate s in optional sandbox object o
if (s == ” && !o) return new o with eager standard classes
if (s == ‘lazy’ && !o) return new o with lazy standard classes
if (s == ‘split’ && !o) return new split-object o with lazy standard classes
evalInFrame(n,str,save) Evaluate ‘str’ in the nth up frame.
If ‘save’ (default false), save the frame chain
shapeOf(obj) Get the shape of obj (an implementation detail)
snarf(filename) Read filename into returned string
read(filename) Synonym for snarf
compile(code) Compiles a string to bytecode, potentially throwing
parse(code) Parses a string, potentially throwing
timeout([seconds])
Get/Set the limit in seconds for the execution time for the current context.
A negative value (default) means that the execution time is unlimited.
elapsed() Execution time elapsed for the current context.
parent(obj) Returns the parent of obj.
wrap(obj) Wrap an object into a noop wrapper.
serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.
deserialize(a) Deserialize data generated by serialize.
js> quit()
$

The first thing you will notice is that all user commands are functions. For example you enter help() instead of help to list help topics and quit() instead of quit to exit the JavaScript shell.

Here is what is outputted by the JavaScript shell that ships with Fedora 14:

$ /usr/bin/js
js> help
function help() {
[native code]
}
js> help()
JavaScript-C 1.7.0 2007-10-03
Command Usage Description
======= ===== ===========
version version([number]) Get or set JavaScript version number
options options([option …]) Get or toggle JavaScript options
load load([‘foo.js’ …]) Load files named by string arguments
readline readline() Read a single line from stdin
print print([exp …]) Evaluate and print expressions
help help([name …]) Display usage and help messages
quit quit() Quit the shell
gc gc() Run the garbage collector
trap trap([fun, [pc,]] exp) Trap bytecode execution
untrap untrap(fun[, pc]) Remove a trap
line2pc line2pc([fun,] line) Map line number to PC
pc2line pc2line(fun[, pc]) Map PC to line number
stringsAreUtf8 stringsAreUTF8() Check if strings are UTF-8 encoded
testUtf8 testUTF8(mode) Perform UTF-8 tests (modes are 1 to 4)
throwError throwError() Throw an error from JS_ReportError
build build() Show build date and time
clear clear([obj]) Clear properties of object
intern intern(str) Internalize str in the atom table
clone clone(fun[, scope]) Clone function object
seal seal(obj[, deep]) Seal object, or object graph if deep
getpda getpda(obj) Get the property descriptors for obj
getslx getslx(obj) Get script line extent
toint32 toint32(n) Testing hook for JS_ValueToInt32
evalcx evalcx(s[, o]) Evaluate str s in optional sandbox object ‘o’
if (s == ” && !o) return new o with eager standard classes
if (s == ‘lazy’ && !o) return new o with lazy standard classes
js> quit()
$

As you can see there is significantly more functionality available with the TraceMonkey JavaScript shell. However a lot of that functionality is specialized and does not provide any additional functionality for JavaScript scripts.

Here is a simple example of a JavaScript script that prints the Fibonacci number for a specified number:

#! /var/tmp/tracemonkey/js/src/shell/js

function fibonacci(num) {
   var curnum = 1
   var prevnum = 0;

   for(i=0;i<=num;i++){
      curnum = curnum + prevnum;
      prevnum = curnum;
   }

   print("Fibonacci number for: " + num + " is: " + curnum);
}

fibonacci(4);


Note that print() function is a JavaScript shell extension which outputs (prints) strings to the console.

The following script extends the previous example to demonstrate how to obtain user input using the readline() and putstr functions.

#! /var/tmp/tracemonkey/js/src/shell/js

function fibonacci(num) {
   var curnum = 1
   var prevnum = 0;

   for(i=0;i<=num;i++){
      curnum = curnum + prevnum;
      prevnum = curnum;
   }

   print("Fibonacci number for: " + num + " is: " + curnum);
}

while (1) {
   putstr("Enter number between 1 and 10, or 0 to exit: ");
   if ((num = readline()) == 0)
      break;
   if (num < 1 || num > 10) {
      print("ERROR: Inputted number outside permitted range");
   } else
      fibonacci(num);
}


There are a number of command line options available for profiling and testing a number of different JIT (Just-In-Time) compilation scenarios and other optimizations.

$ ./js –help
JavaScript-C 1.8.0 pre-release 1 2007-10-03
usage: js [options] [scriptfile] [scriptarg…]
Options:
-h Display this information
-z Create a split global object
Warning: this option is probably not useful
-P Deeply freeze the global object prototype
-s Toggle JSOPTION_STRICT flag
-w Report strict warnings
-W Do not report strict warnings
-x Toggle JSOPTION_XML flag
-C Compile-only; do not execute
-i Enable interactive read-eval-print loop
-j Enable the TraceMonkey tracing JIT
-m Enable the JaegerMonkey method JIT
-p Enable loop profiling for TraceMonkey
-d Enable debug mode
-b Print timing statistics
-t Interrupt long-running execution after seconds, where
<= 1800.0. Negative values indicate no timeout (default). -c Suggest stack chunk size of bytes. Default is 8192.
Warning: this option is currently ignored.
-o

Consider the following simple script to print a number from 0 to 9999 and print the elapsed time on completion.

for (i=0; i< 10000; i++) {
   print (i);
}

print("Elapsed time: " + elapsed() + "ms");


Check out the difference in the elapsed time when any of the JIT options are enabled for the above script. It is significant.

You can also interactively load and execute a JavaScript script in the JavaScript shell using a number of different techniques as demonstrated below.

js> load(“fibonacci.js”)
Enter number between 1 and 10, or 0 to exit: 0
js>
js> var file=read(“fibonacci.js”)
js> eval(file)
Enter number between 1 and 10, or 0 to exit: 0
js>
js> evalInFrame(6,file, false)
Enter number between 1 and 10, or 0 to exit: 0
js>

Well that should be all you need to know to start using the TraceMonkey JavaScript shell on Fedora 14.

Enjoy!
 

1 comment to Updating the Fedora 14 JavaScript Shell

  • Gas

    I am very thankful to this topic because it really gives great information