Using my Java 7-to-6 backporter on several of my open source project worked perfectly. Until now. Suddenly I got a strange NoClassDefFoundError for java.lang.ReflectiveOperationException. I never heard of this exception and I thought that Animal Sniffer would find all classes not belonging to JDK 6.

A quick look in the source code showed that this exception is used nowhere. Ok, let’s use javap to examine the bytecode. After some searching around and dabbling at it, I found the ominous class in the StackMapTable.

Now what’s this beast? Never heard of it. But here’s the class file specification for it. That’s not exactly simple to follow. But Stack Overflow has an understandable explanation.

In very short words: It stores the possible runtime types of the variables at different points in the code. This is used for the verification of the bytecode. It was introduced in Java 6/7 to improve the performance of the verification process. (Before, this information was deduced when a class was loaded through dataflow analysis.)

But how is this class coming into the StackMapTable when it is used nowhere in the code? It came from this code fragment:

public Object instantiate(Class<?> clazz) {
    try {
        return clazz.newInstance();
    } catch(InstantiationException | IllegalAccessException e) {
        ...    
    }
}

In the catch block, the type of e is the most specific common supertype of InstantiationException and IllegalAccessException. Before Java 7, this was Exception but since Java 7 it’s the newly introduced ReflectiveOperationException. That’s why this code cannot run on a Java 6 JVM even when in the source code no offending class is used.

I tried several ideas to change the bytecode to make it runnable on Java 6. But to no avail. Finally, I gave up. The backporter just scans the constant pool for this exact class and throws an error message if any occurrence is found. There’s no guarantee that this cannot happen tomorrow with another such newly introduced class.

So the learnings from this short episode are:

  • A hack is a hack and can fail any time.
  • Java bytecode is a very interesting yet not a simple topic.
  • Animal Sniffer is not perfect.
  • ReflectiveOperationException is a very useful new exception when dealing with reflection: Just catch and throw this one instead of ClassNotFoundException, IllegalAccessException, NoSuchMethodException et al.