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.