Example: Finding Thrown Exceptions

Let's write a pass that determines the set of uncaught exceptions that can be thrown by a method.

We will use a flow-insensitive technique. The problem is complicated by the fact that exceptions that are thrown can be caught within the method itself, and we don't want to include those exceptions in our result. Exception handlers are attached to the basic blocks that they protect, so we need to visit the basic blocks to learn about the exception handlers.

We will use one visitor object that is both a BasicBlockVisitor and a QuadVisitor. The visitBasicBlock routine will cache the current basic block and call visitQuads() to visit the quads.

import joeq.Compiler.Quad.*;
import joeq.Util.Templates.ListIterator;
import joeq.Class.*;
import java.util.*;
import joeq.Main.Helper;
 
public class FindThrownExceptions extends QuadVisitor.EmptyVisitor implements BasicBlockVisitor {
    
    private Set thrownExceptions = new HashSet();
    private BasicBlock currentBasicBlock;
    public void visitBasicBlock(BasicBlock bb) {
        currentBasicBlock = bb;
        Helper.runPass(bb,(QuadVisitor)this);
    }
    public void visitExceptionThrower(Quad q) {
        ListIterator.jq_Class i = q.getThrownExceptions().classIterator();
        while (i.hasNext()) {
            jq_Class c = i.nextClass();
            // need to make sure this exception type is not caught by a handler
            if (currentBasicBlock.getExceptionHandlers().mustCatch(c) == null) {
                thrownExceptions.add(c);
            }
        }
    }
    public String toString() { 
        return "Possible thrown exceptions:"+thrownExceptions; 
    }
 
    public static void main(String[] args) {
        jq_Class[] c = new jq_Class[args.length];
        for (int i = 0; i < args.length; i++) {
            c[i] = (jq_Class)Helper.load(args[i]);
        }
 
        FindThrownExceptions fte = new FindThrownExceptions();
 
        for (int i = 0; i < args.length; i++) {
            Helper.runPass(c[i], (BasicBlockVisitor)fte);
        }
 
        System.out.println(fte);
    }
}

Now let's take a closer look. Our class, FindThrownExceptions, both implements BasicBlockVisitor and extends QuadVisitor.EmptyVisitor. This means that in order to call Helper.runPass, we need to cast it to indicate which kind of Visitor we mean it to be treated as.

There are two fields. The first is thrownExceptions, which contains the exceptions seen thus far. It is initialized to an empty set. The second is currentBasicBlock, which keeps track of the current basic block as we iterate through the quads. We need to keep track of the current basic block because quads do not contain a reference to their enclosing basic block, and we need the enclosing basic block to find the exception handlers.

The visitBasicBlock method simply sets the currentBasicBlock field and then runs the Quad visitor on itself. That includes visitExceptionThrower, which matches quads that can throw exceptions. We start by iterating over the types of exceptions that the quad can throw, obtained via getThrownExceptions(). For each of those exception types, if that type is not necessarily caught by an exception handler, we add it to the set thrownExceptions.

Lastly, the main routine loads the classes from the command line, find all uncaught exceptions they throw (the thrownExceptions set only grows in size), and prints them out.

<-- previous home next -->