April 19, 2013

Local Variable Debugging with see.js

Modern Javascript debuggers are fun to use because you can inspect the program state at any time without stopping at a breakpoint. The problem is, you can only inspect global state. What if you want to see local variables? Then you're back in 1980, setting breakpoints each time you want to see your closure state again.

This is a problem that strikes Coffeescript programmers in particular, since all variables in Coffeescript are local variables wrapped in anonymous closures. All program state that has not been explicitly been made global becomes invisible to debugging consoles.

The solution is see.js. It is a simple debugger that I have written that lets you interact with an eval closure that you can insert at any scope you want to view. In Javascript it is pretty common to use closures to encapsulate a lot of state - even your whole program. With see.js, you can write clean code like this without losing the ability to debug.

More details below.

Using see.js

To use see.js, just include it to define the single debugging object see:

<script src="https://raw.github.com/davidbau/see/master/see.js"></script>

Then insert eval(see.here) in your program at the scope of interest:

(function() {
  eval(see.here);  // Debug variables visible in this scope.
  var private_var = 0;
  function myclosuremaker() {
    eval(see.scope('inner'));  // This scope can also be chosen.
    var counter = 0;
    return function() { ++counter; }
  }
  var inc = myclosuremaker();
  inc();
})();

Within the see debugger, "private_var", "inc" and "myclosuremaker" are all visible symbols that can be explored and altered. The inner closure variable "counter" is still hidden, but it can be inspected from the nested context by typing ":inner" in the debugger.

You can also load see.js on any page with this bookmarklet [See] (drag it to your bookmark bar, or click it now to see it on this blog). Of course, it only has access to global scope unless you have a way to eval(see.here) in a different local scope. That's a snap if you're developing the code: just insert window.see&&eval(see.here);, which will come alive next time it runs with the bookmarklet active. If you're not modifying the code, a traditional debugger can help you eval the hook in the context of a breakpoint.

The see debugger is simple, but it has a couple other features. It supports CoffeeScript, and and it include a nice compact object pretty printer that can be used completely independently of the debugger panel by using see.repr(obj).

More documentation here.

Why?

I wrote see.js as part of a little project to put together an educational programming environment for kids based on CoffeeScript and turtle graphics (for example, try this page with a turtle, or read this quick reference guide for the kids).

CoffeeScript is wonderful as a first learning language because of its lack of punctuation: no semicolons or parentheses are needed to get something running. Many of the unnecessary mysteries of Javascript are gone. However, CoffeeScript has the disadvantage that variable state is totally hidden from the debugging console because it is always wrapped in a closure. You cannot learn about how variables work using the Chrome debugger with a CoffeeScript program.

The see.js debugger solves this problem while also allowing students to interact with a debugger without pressing control-shift-J and without having to learn how to pick through state in different iframes: see.js can be programmatically focused to the right spot.

Posted by David at April 19, 2013 10:17 PM
Comments
Post a comment









Remember personal info?