I earn a living writing C++ code. Code that twiddles pixels, mostly, and, while I write the occasional web app page, I am not a web application developer. Yet I am an avid user of JavaScript.
Long before node.js rose to its popularity, Microsoft Windows users had access to all the power of a scripting engine on a command line– cscript.exe. It is a very convenient and powerful language+host combination to develop simple utility scripts. Much faster than writing CLI (Command Line Interface) C++ utility. CScript.exe is on all modern flavors of Windows, thus it is as portable (Windows-vise) as an equivalent C++ EXE.
This post is a story in three parts of how I ended up implementing a JavaScript REPL console with simple debugger REPL and breakpoints. In this first part, I am going to explain how I arrived at the decision to write JSREPL and how it all started. The full source code of JSREPL is freely available under GPL 2 license on github. If you’d rather not follow my journey, you can directly jump there.
Motivation
Visual Studio is the default development and debugging tool for JavaScript, when working with cscript.exe. While it is a very powerful and full featured debugger, rapid development and debugging is hindered by two facts – first, there is no support for cscript.exe JavaScript projects in Visual Studio.
Second, the just-in-time debugging via //X
command line option is cumbersome. It pops a pop-under dialog where you need to select a version and instance of Visual Studio to use for debugging every time a new debugging session is started. And what about that startup time of Visual Studio 2010?
To me, the power of JavaScript is in its immediate zero-compile-time nature. I would like to be able to move fast, in a rapid edit-run-fix succession. Such desire left me searching the internet for an alternative JavaScript debugger. There are several mentions of an older Microsoft Script Debugger, which is not as feature rich as Visual Studio and is not supported on newer versions of Windows, but nothing else along the lines of my hopes of simple REPL (read-evaluate-print-loop) style debugger.
Thus, I decided to write one myself.
Choices
I had written a trivial JavaScript REPL script a few years ago (see below) and I wanted to continue in a similar direction – command line interface, simple quick and reuse as much of JavaScript engines' capability as possible.
Figure 1. An example of my early JavaScript REPL.
1 WScript.StdOut.Write("# ");
2 while ((cmdLine = WScript.StdIn.ReadLine()).toLowerCase() != "quit" ) {
3 try {
4 WScript.Echo("\n",(eval(cmdLine)));
5 } catch (err) {
6 WScript.Echo("\n", err.toString());
7 }
8 WScript.StdOut.Write("# ");
9 }
The early REPL was useful to me in many ways – from debugging regular expressions to analyzing COM object API behavior. As the time went by, I have added more features – multiline mode (line continuation character) and some global utility functions (print, println, string prototypes, etc.) The script became more useful, but it was still severely lacking a debugging facility —at the very least, some ability to interrupt execution of the program and inspect variables.
So I had to write the debugger. Looking at the Windows Script Runtime documentation on MSDN, one choice became evident – write a full scale debugger in C++ using Script Runtime Hosting API. Even though I write C++ for living and COM is flowing in my blood, I was not too excited to write a full scale host for the Scripting Runtime, then have to register it on the target machine, etc. I decided to look for a simpler solution.
The simpler solution was to evolve my early REPL.
First Steps
Since I had a quite useful REPL going, I wanted to extend it rather than start from scratch. The inventory of the new functionality came down to the following:
- Facility to load script files into REPL – a load command
- Facility to list source code of a function – a list command
- Facility to set breakpoints and a debug REPL
The load command seemed simple enough:
Figure 2. Simple function to load script from file
1 function load(filePath) {
2 var fso = new ActiveXObject("Scripting.FileSystemObject");
3 if (fso.FileExists(filePath)) {
4 return fso.OpenTextFile(filePath).ReadAll();
5 }
6 return null;
7 }
8
Then, just eval
the returned text inside the main loop to create all the objects in the global scope:
Figure 3. Loading of a script via eval
1 eval(load("Test1.js"));
The load
function could be further enhanced by searching the JSREPL folder and current PATH
to load the file. An added benefit of the load
function is that now I can easily break my REPL implementation into smaller files and not bother with WSF format.
A list command did not seem too hard either – just obtain a reference to function
object, then do toString()
on it to obtain the string
representation, split on newline, etc. If you are following along the source code, this facility is in dbg.js – by this time, my JSREPL
project has three files – js.cmd (more on .cmd part later), dbg.js and util.js.
Figure 4. Function to display source code
1 listFunction: function (fn) {
2 var fnText = fn.toString().split(/\n\r|\r\n/);
3 for (l in fnText) {
4 println(l.rJustify(4), " ", fnText[l]);
5 }
6 }
In the above code, there are a couple of utility functions – rJustify
and println
. Both are in util.js. The function is a method of the DBG
class, hence the syntax.
The js.cmd is a JavaScript file, but it is executed by cmd.exe first, such that cscript.exe is invoked with specific command line options. This is done via some trickery:
Figure 5. Cmd to cscript bootstrap code
1 @set @a=0
This code needs a bit of explanation. It is not my invention – I saw it somewhere circa 2004 – and it is very clever. Some scripting interpreters, such as Python or Perl, have a special command line option to skip one or more lines of source file. This allows to have batch file (shell script) invoke itself via the interpreter. Cscript.exe offers no such option. So the first line of the code serves the purpose of fooling cscript.exe into the JavaScript comment, while at the same time keeping cmd.exe quiet about it.
Cmd.exe reads the first line as “quietly assign 0 /* to variable @a”, while cscript.exe reads the line as “conditional compilation variable @set, followed by assignment to conditional compilation variable @a, then followed by a JavaScript comment”.
Lines 2 through 4 are ignored by cscript.exe, since that is JavaScript comment. However, cmd.exe reads line two as “quietly unassign variable @a." Then line three “quietly execute cscript.exe passing this file as first parameter”, and, finally, line four tells cmd.exe to “quietly exit”. Pretty neat.
Let’s take an inventory here. By now, JSREPL is quite a useful interactive environment – I can load scripts and even see exactly where I want to place the breakpoints. In part two, I will break down the break points (pun?) and debug REPL.