Friday, December 10, 2010

when bytecode analysis is too much

There are many tools for static code analysis available. Recently I started using PMD and Findbugs. The main difference between these two libraries is that one of them (Findbugs) operates on bytecode produced by javac, whereas PMD works on abstract syntax tree of java source files, without the need of their compilation.

Today I faced a problem, which allowed me to understand static code analysis and a way java compiler works a little bit more. 

Reporting tools used in my company declacred many violations of our home-grown Findbugs rule. In brief: its purpose was to recognize all the empty catch clauses. As this rule behaviour was very well unit tested at an angle of various scenarios, it was the most violated rule. What was even more strange: it reported violations not only on empty catch blocks as it was supposed to, but also on perfectly legal switch statements, which used enums as arguments. Here is an example:

public void testIt(DummyEnum anEnum) {
        switch (anEnum) {
            case ONE:
                // do sth
                break;
            case TWO:
                // do sth else
                break;
            default:
                throw IllegalStateException("dummy exception");
        }
}

Usage of javap and java disassembler revealed the cause of the problem. It turned out, that behind the scenes enum types are internally mapped to static arrays. Switch clauses, which use enums are in turn translated into series of try-catch (NoSutchFieldError) blocks - empty catch blocks. Analysis of exception tables provided by javap clearly shows where to goto when an exception occurs inside switch.

public void testIt(DummyEnum anEnum) {
        switch (EnclosingClass.$SwitchMap$DummyEnum[this.anEnum.ordinal()]) {
            case ONE:
                // do sth
                break;
            case TWO:
                // do sth else
                break;
            default:
                throw IllegalStateException("dummy exception");
        }
}
As the PMD library does not operate on bytecode, it was much easier to rewrite this rule (using XPath expression) rather than patch existing Findbugs rule. In this case usage of Findbugs appeared to be exaggerated.

No comments: