Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

JavaScript REPL for Windows: Part 3 – Dynamic Breakpoints

0.00/5 (No votes)
25 Aug 2011 1  
Dynamic Breakpoints in JavaScript REPL for Windows

This is part three of a three part post about JavaScript REPL console for Windows. In part one, I explained motivation and some initial steps, in part two, I added debug REPL and static breakpoints. In part three, I am going to add the ability to set breakpoints dynamically from within JSREPL.

As mentioned previously, the full source code of JSREPL is freely available under GPL 2 license on github.

Dynamic Breakpoints

While static breakpoints offer good benefits of being flexible, they are, well, static. When I am debugging a script, it is hard to predict where all the breakpoints would have to be placed. So, if I decide that I need another breakpoint in the middle of the debugging session, I need to edit the source code and restart the session.

It would be nice if I could set the breakpoints on the fly without the need to restart the session. It turns out, there is an easy way to do that for functions declared at the global scope; either global functions or anonymous functions referenced by global variables. Furthermore, it is even possible to do this for anonymous functions declared within such global functions, for example, object methods. There are some caveats to the latter part, though.

Well then, my JSREPL is getting three new commands:

  • bp – for adding new breakpoints
  • bd – for deleting breakpoints
  • bl – for listing breakpoints

The logic of adding a new breakpoint as well as removing one is quite simple – just figure out all the lines that the function needs to have breakpoints and generate a new string with source code for the function. Eval that at the global scope and subsequent invocations of the function will trigger breakpoints.

There is one fine point in all of this: whether function is anonymous or not. But that is easily determined by the parseFunction method from part two. If the function is anonymous, then the eval should be an assignment to a global variable. Otherwise, it is just a function declaration.

Figure 14. Method to update breakpoints
1     updateFnBreaks: function (bpInfo, bpLine) {
2         var lineNos = Arr(bpInfo.breaks).map(function(eBr) { return eBr.line; }).a;
3         if (typeof(bpLine) != "undefined") {
4             lineNos.push(bpLine);
5         }
6         lineNos.sort();
7 
8         var fnLines = bpInfo.savedFunc.slice(0);    // copy the array
9         var bpLabelPrefix = bpInfo.name + "(";
10         for (iLN in lineNos) {
11             var offset = lineNos[iLN] + 
               parseInt(iLN);    // compensating for lines inserted already
12             if (offset < fnLines.length) {
13                 var bpLabel = bpInfo.name + 
                   "(" + lineNos[iLN].toString() + ")";
14                 fnLines.splice(offset, 0 /*=toDelete*/, 
                   "eval(dbgBreak(\"" + 
                   bpLabel + "\")) // <dbg_break>");
15             } else {
16                 println("ERROR: line ", lineNos[iLN], 
                   " is outside of the function");
17                 return null;
18             }
19         }
20         return fnLines.join("\n\r");
21     }

To illustrate the dynamic nature of the breakpoints, let’s look at another example. This time, we are dealing with class method.

Figure 15. Source code for the example of dynamic breakpoints
1 classA = function (n) {
2     return {
3         name:  n,
4         who:  function () {
5             println("Name=", this.name);
6             return this.name;
7         }
8     };
9 };

In this case, I would like to set a breakpoint on line 6, but that is in the anonymous function, which happens to be a class method. Here is how that worked out for me:

Figure 16. Session log of dynamic breakpoint demo
1 # list classA
2    0 function (n) {
3    1     return {
4    2         name:  n,
5    3         who:  function () {
6    4             println("Name=", this.name);
7    5             return this.name;
8    6         }
9    7     };
10    8 }
11 # bp classA 5
12 # var a1 = new classA("John")
13 
14 # a1.who()
15 Name=John
16 
17 dbg break – "classA(5)"  ? – displays list of debugger commands.
18 dbg> w
19 Call stack:
20 
21 Level Function
22 ~~~~~ ~~~~~~~~~~~~
23     0 <anonymous>()
24 dbg> a1.name
25 John
26 dbg> g
27 John
28 # bl
29 Active breakpoints:
30 
31 Id  Function(line)
32 ~~~~ ~~~~~~~~~~~~~~
33    0 classA(5)
34 # bd 0
35 # bl
36 Active breakpoints:
37 
38 Id  Function(line)
39 ~~~~ ~~~~~~~~~~~~~~
40 # a1.who()
41 Name=John
42 
43 dbg break – "classA(5)"  ? 
   – displays list of debugger commands.
44 dbg> g
45 John
46 # var a2 = new classA("Peter")
47 
48 # a2.who()
49 Name=Peter
50 Peter
51 #

Setting the breakpoint (listing line 7) triggers the break into debug REPL (listing line 17). However, removing the breakpoint (listing line 43) does not remove the breakpoint from the method of object a1 (listing lines 40 – 44). However, creating a new instance of classA after removing the breakpoint acts as expected (listing line 48).

This is it. While there are some caveats to dynamic breakpoints, it is still a very useful addition to JSREPL.

Next Steps

JSREPL is a work in progress and will be for quite some time. I am refining the functionality and addressing more corner cases as I run into those. If you use JSREPL and run into issues, please report them on the github issues page.

I already have plans for the future – I would like to add enabled/disabled flag to the breakpoint, such that the breakpoints that cannot be deleted, could at least be disabled. I would like to add an indicator of the current breakpoint in the function source listing. The wiki on github needs updating as well.

Thank you for reading and happy debugging.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here