Thursday, June 24, 2010

Updating ASM

jSuneido uses ASM to generate Java byte code. I was using 3.1 and the current version is 3.3

I wasn't sure if this would be easy or hard. It turned out to be somewhere in between.

First I tried to update my Eclipse plugin. That was a little confusing because I had three update sites showing ASM -,, and I'm guessing the one is something internal to Eclipse. I thought would be the correct location, but it didn't have the latest version.

Next I downloaded asm-3.3.jar but that didn't work, I needed to download and extract asm-all-3.3.jar  This wasn't entirely a surprise because previously I was using multiple asm jar files. I'm not quite sure why the asm-3.3.jar is available as a separate download, but not the asm-all-3.3.jar

Of course, tests failed. The first message was:

java.lang.IndexOutOfBoundsException: Trying to access an inexistant local variable 0
at org.objectweb.asm.tree.analysis.Frame.setLocal(Unknown Source)
at org.objectweb.asm.tree.analysis.Analyzer.analyze(Unknown Source)
at org.objectweb.asm.util.CheckMethodAdapter$1.visitEnd(Unknown Source)
at org.objectweb.asm.util.CheckMethodAdapter.visitEnd(Unknown Source)
at org.objectweb.asm.tree.MethodNode.accept(Unknown Source)
at org.objectweb.asm.commons.TryCatchBlockSorter.visitEnd(Unknown Source)
at org.objectweb.asm.MethodAdapter.visitEnd(Unknown Source)

[I thought "inexistant" was a typo, but it appears to be the French version of nonexistent.]

I searched for other people having this problem and found:
ClassWriter.COMPUTE_FRAMES causes java.lang.ArrayIndexOutOfBoundsException

I didn't resolve this. As a workaround I use CheckClass(visitor, false) to turn off the data flow checks. I am probably doing something wrong, but the byte code passes Java's verification and it runs ok, so presumably it's nothing too serious? 

The next problem was that you now have to call visitTryCatchBlock before defining any of it's labels. At first I thought that was easy to fix, just move the calls from the end of the try-catch block to the beginning. But one test failed - with nested try-catch blocks. The problem is that the JVM searches the exception table in order. So inner blocks must come before outer ones. By emitting at the end of the blocks I got the right order. But now that I was forced to emit at the beginning of the blocks, the order was wrong. 

This stumped me for a while. The problem is that jSuneido (and cSuneido) compiles in a single pass, generating code as it parses. This avoids building a syntax tree. But it doesn't give much flexibility in the order of generating code. I was having scary thoughts of having to make two passes, or switching over to building an intermediate tree. Of course, I could just go back to ASM 3.1, but in the long run I'd probably need to upgrade (e.g. for invoke dynamic support).

Again I searched for other people having this problem and didn't find anything. I was going to post on the ASM Tracker, but before I did I figured I'd better make sure there wasn't an existing post. As it turned out there was: Provide exception table sorting functionality (I'm not sure why my Google searches didn't find it.) Not only had someone else had the same problem, but they'd come up with a solution, and it was now included in ASM. All I had to do was add TryCatchBlockSorter. It's actually quite simple, once you know that the table is accumulated in a way that allows it to be sorted.

Still, I'm not sure why they added this requirement. It seemed to work fine without it in the previous version.

After that, I just had to fix a few tests to take into account the slight differences in the generated try-catch code and I was back in business.

No comments: