BZ #125: ClassFormatError with generated bytecode

Status fields:

creation_ts:2009-03-23 22:24
component:verifier
version:unspecified
rep_platform:All
op_sys:All
bug_status:RESOLVED
resolution:FIXED
reporter:michi@complang.tuwien.ac.at
It seems we have some problems with the Javassist library which is used quite often (by
Hibernate or Tapestry for example). It generates bytecode at runtime and loads it. The
generated bytecode is different to what we are used to load and (maybe) triggers some
hidden bugs. I'll have to analyze this further.

This is the exception Cacao is generating:
Exception in thread "main" java.lang.ClassFormatError: Foo (Arguments can't fit into
locals)
   at java.lang.VMClassLoader.defineClass(Native Method)
   at java.lang.VMClassLoader.defineClassWithTransformers(VMClassLoader.java:454)
   at java.lang.ClassLoader.defineClass(ClassLoader.java:471)
   at java.lang.ClassLoader.defineClass(ClassLoader.java:436)
   at LoadBytes.load(LoadBytes.java:43)
   at LoadBytes.main(LoadBytes.java:53)

Comment #1 by michi@complang.tuwien.ac.at on 2009-03-23 22:50:45

I have written a testcase to trigger the problem. Once the check in our loader is
removed we get a verification error explaining what the real problem is. This is the
exception I am talking about:

java.lang.VerifyError: (class: Foo, method: <clinit> signature: ()V) Not enough local
variables for method arguments
   at java.lang.reflect.VMField.getInt(Native Method)
   at java.lang.reflect.Field.getInt(Field.java:374)
   at PR125.test(PR125.java:36)

The problem is the class initializer <clinit> which should be marked ACC_STATIC but
isn't. It therefore receives at least one argument (the this reference) which needs at
least one local variable. But the max_locals for the method is set to 0 which leads to
the verification error above.

The strange thing is that HotSpot not only loads the bytecode, but also successfully
calls the buggy <clinit> method as class initializer. Funny thing, a "non-static class
initilaizer".

This is the testcase I was talking about:
http://mips.complang.tuwien.ac.at/hg/cacao/rev/c657cc463135

Comment #2 by michi@complang.tuwien.ac.at on 2009-03-24 01:13:43

I have resolved the problem by looking at the VM spec (section 4.6) which states that
all (except ACC_STRICT) access flags need to be ignored for class initializers. Ignoring
them also means setting the ACC_STATIC flag (section 3.9) per default. I really wonder
about this stuff sometimes.

This is the fixing changeset:
http://mips.complang.tuwien.ac.at/hg/cacao/rev/347c73da29cf

This is what the VM spec says:
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#1513

PS: Remember that you need the second edition of the VM spec. Don't even look at your
first edition hardcopy like I did.