Wednesday, August 29, 2018

Control flow graphs in N4JS

The N4JS IDE comes with several tools to get insights into the source code such as the AST and the control flow. In this post we present the control flow graph view.



When learning a programming language, we probably start with a small Hello World! program. From there on, we learn not only language keywords and libraries, but also get an implicit understanding in which order statements and expressions are executed. This order is called control flow. For instance, we learn the effects of if and for statements which are also called control statements, since they have a big influence on the execution order. The most difficult statement in this respect is probably the try-finally control statement, which is explained later.


Hello World


function f() {
  console.log("Hello World!");
}

Let's have a look at the Hello World! from a control flow perspective, first. The source code of Hello World! is simple, but it already consists of three important elements:
  • the method call console.log,
  • the nested argument "Hello World!", and
  • the function body of f() which contains the two elements mentioned above.
The function body is also called control flow container.


The image above shows the control flow graph of the Hello World! example. The method call is separated into the receiver and its property log. The next element the control flow goes to is the argument "Hello World!" until it reaches the method call of log as the final element.


Loops


The example above showed the succeeding control flow of some code elements. This control flow gets more interesting in statements that introduce branches such as loop statements do. In the example below, the control flow of a for-loop is shown. To indicate the start and end of the function, and also the body of the loop, the function calls start(), loop() and end() are used.

function f() {
  start();
  for (var i=0; i<2; i++) {
  loop();
  }
  end();
}


The control flow of the loop example shows branches and merges. After the condition i<2, either the body is entered via the edge named LoopEnter, or the loop is exited. In case the body was entered, the control flow first targets the call to loop() and then goes back to the entry of the condition.


Try-Finally


Finally blocks have tricky semantics since they can be entered and exited in two specific ways: normally and abruptly. Abrupt control flow occurs after a return, continue, break or throw statement. These will introduce a jump that targets either the end of the function or the entry of a finally block. In case the control flow jumps to a finally block, this finally block will be executed normally. However, since the block was entered abruptly, it will exit abruptly again. This means, that there will be a second jump from the exit of the finally block to either the end of function or the entry of the next finally block.


The following example shows this behaviour by using some dead code elements, which have a grey background colour in the graph image. These dead code elements are not reachable, since there is no normal control flow path that exits the try block.


function t() {
  "start";
  try {
  2;
  return;
  3;
    } finally {
  "finally";
  }
    "end";
}


Final catch


In case you ever wondered how a thrown exception can be caught without using a catch block, have a look at the final example. It is true that once a finally block was entered abruptly, it can only be exited abruptly. However, the kind of abrupt control flow might be changed. In the example below, it is changed from throwing an exception to breaking the loop. Of course, after the loop was exited due to the break statement, the control flow is normal again and the thrown exception remains without effect. Hence, the last statement "end" is executed which would have been skipped otherwise.



function t() {
  "start";
  do {
    try {
      2;
      throw "exception";
    } finally {
      break;
    }
  } while (4);
  "end";
}













The interested reader would find more details about the N4JS flow graphs and their implementation in the N4JS Design Document, Chapter: Flow Graphs.


by Marcus Mews