Sundararajan's Weblog

JavaScript debugging tips (for Mustang context)

Guest Author

Mustang (Java SE 6) is co-bundled with Mozilla Rhino based JavaScript engine as an example implementation for javax.script API.

There are 3 use cases within JDK

There is already an experimental web application environment that uses JavaScript - http://phobos.dev.java.net.

So, we may expect that JavaScript will be employed within the JDK context more often. How about debugging support for JavaScript engine in JDK context? For Firefox, there are atleast two debuggers (that I know of!):

  1. Venkman
  2. Firebug

Rhino comes with it's own debugger -- but in Mustang, Rhino's tools are not included. Also, javax.script API does not include debugger API (yet?).
For Rhino in Mustang, we have to resort to other ways to debug scripts including good old println/printf style. Few debugging tips and tricks are here...

  1. Use jrunscript in interactive mode for debugging.

    You can use load function to load your script files (or URLs). In the interactive mode, you can call specific script functions or evaluate various expressions.

  2. print or alert debugging!

    Use good old printf/System.out.println/alert/echo style debugging! In Mustang Java, there are built-in print and println functions always defined. If you have scripts that use alert, you can consider defining something like this:

    // define alert to be same as println function
    var alert = println;

    Note that 'print' and 'println' functions print the message to output writer configured in the script context (recall that "context" is a pre-defined variable initialized with the current ScriptContext object). If you configure different output writer (may be, the one that writes messages to a file or socket etc.), you can make all print/println messages to go there.

  3. Use
    to view source code of various functions.

    // viewing function sources in jrunscript prompt.
    // built-in functions are shown as "native code"
    // But, you can get arity (number of arguments)
    // of the function
    js> eval.toSource()js> eval.toSource()
    function eval() { [native code for eval, arity=1] }
    // for script functions toSource() shows full source
    js> function add(x, y) { return x + y }
    js> add.toSource()
    function add(x, y) {return x + y;}
    // for Java methods we get signature as part of "source"
    js> var v = new java.util.Date()
    js> v.getDay.toSource()
    function getDay() {/\*
    int getDay()
    // for overloaded Java methods, toSource() shows signatures
    // for all overloads
    js> var out = java.lang.System.out
    js> out.println.toSource()
    function println() {/\*
    void println(boolean)
    void println(char)
    void println(int)
    void println()
    void println(long)
    void println(java.lang.Object)
    void println(java.lang.String)
    void println(char[])
    void println(double)
    void println(float)

  4. JavaScript object fields and array elements can be walked by

    for(var i in obj) { print(obj[i]); }

    like loop.

    I frequently print all fields of an object (or elements of array) this way. Note that you will get object's methods (which are function valued properties) as well. To filter these, you can use typeof operator. You can also filter properties using property name pattern/index.

  5. Use JavaScript's support for higher-order functions.

    JavaScript functions can be passed as arguments and returned as values. Functions can be called by
    apply or
    call functions. Functions are first-class values that can be stored in variables. You can use these to implement JavaScript AOP. You can use "interceptors" to print debug trace output. For eg. function entry/exit can be printed (along with arguments, if desired). For example, the following is a variation of JavaScript AOP referred in above link..

    var Aspects = new Object();
    // calls "before" interceptor function before calling function
    // name specified by fname. The function is expected to be a property
    // of object 'obj'
    Aspects.addBefore = function(obj, fname, before) {
    var oldFunc = obj[fname];
    var newFunc = function() {
    return oldFunc.apply(this, before(arguments, oldFunc, this));
    // store oldFunc for restore purpose...
    newFunc.oldFunc = oldFunc;
    obj[fname] = newFunc;
    Aspects.restore = function(obj, fname) {
    obj[fname] = obj[fname].oldFunc;
    // tracing function entry
    function traceEntry(args, oldFunc, thiz) {
    // print the name of the function
    print("entering " + oldFunc.name);
    // print arguments
    var str = "";
    for (var i = 0; i < args.length; i++) {
    str += args[i] + ", ";
    print("arguments " + str);
    // return argument array - used by oldFunc.
    return args;

    With the above code in a debug library (say "debug.js"), the client code can write something like:
    function add(x, y) { return x + y; }
    // just add two arguments
    add(3, 4);
    Aspect.addBefore(this, "add", traceEntry);
    // call add -- prints debug output on entry
    add(3, 4);
    // restore old "add" function without debug output
    Aspect.restore(this, "add");

  6. Tracking global variables: It is better to avoid globals as much as possible.

    Use objects instead. It is easy to accidentally create globals in JavaScript.
    For example,

    function f() {
    x = m(); // forgot 'var' before x, x is global!
    // rest of the code...

    While it may be possible to spot "unwanted" globals by code inspection, we can
    do better than that. It is possible to check global variable assignments and accesses
    using javax.script.Bindings used for global variable storage.

    import javax.script.\*;
    import java.io.\*;
    public class Test {
    public static void main(String[] args) throws Exception {
    // create a new ScriptEngineManager
    ScriptEngineManager m = new ScriptEngineManager();
    // get JavaScript engine instance
    ScriptEngine jsEngine = m.getEngineByName("javascript");
    // set a "debug" bindings for global variables
    jsEngine.setBindings(new DebugBindings(), ScriptContext.ENGINE_SCOPE);
    // eval code from a java.io.Reader object.
    jsEngine.eval(new FileReader(args[0]));

    The DebugBindings class could look like

    // a simple Bindings implementation that prints debug output
    // whenever a variable is accessed or assigned.
    import javax.script.\*;
    public class DebugBindings extends SimpleBindings {
    @Override public Object put(String name, Object value) {
    Object res = super.put(name, value);
    System.out.println("Global assign: " + name);
    return res;
    @Override public Object get(Object key) {
    Object res = super.get(key);
    System.out.println("Global access: " + key);
    return res;

    When running the Test class with the following t.js
    script file,
    function h() {
    // global variable assign
    x = 32;

    we get the following output..
    Global assign: context
    Global assign: print
    Global assign: println
    Global access: javax.script.filename
    Global assign: h
    Global access: h
    Global assign: x

    Note that debug output is printed for function "assignments" as well. Note that
    global functions are global variables with "function" value.

  7. Debugging JavaScript objects

    Because JavaScript is dynamically typed language, we can replace an object with any other object that "looks" like the "original". i.e., the replacement objects should just support same methods, properties -- but can do anything. (If it walks like a duck and quacks like a duck, it must be a duck). You can wrap actual object with a "debuggable" object (whose methods print debug/trace output) -- so long as debug
    wrapper objects supports same methods, properties. Usually, it is very easy to wrap an object with another object in JavaScript. But, if your object supports properties (a.k.a fields) it becomes tricky to wrap the same - for example, you may want to wrap XMLHttpRequest that has properties. But, you can use JSAdapter
    and "hook" all property or method access or assignments.

Join the discussion

Comments ( 2 )
  • guest Wednesday, July 5, 2006
    Just a question related to this blog,
    do you know why javax.script.Bindings
    inherits from java.util.Map.

    This prevent any other implementation than
    a map.

    By example there is no way to add infinite
    identifiers like a1, b3, etc. (letters+numerics)
    to represent a cell in a spreadsheet.

    Because bindinds are a map, bindinds must
    have a size and a way to iterate on it.

    Rémi Forax

  • A. Sundararajan Thursday, July 6, 2006
    Hi Remi Forax: I think there are two ways to handle this:
    1. Override only get, put methods of Map. i.e., return "incorrect size" (may be even 0) and empty-iterator. Script engines would not call iterator or size in most instances. Variable accesses will result in Bindings.get() call and variable assignments/updates will result in Bindings.put() call.
    2. Expose cell access as a built-in function for your scripting language. For example, in JavaScript you can expose spreadsheet access a built-in function.
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.