SerialVersionUIDAdder generates incorrect serialVersionUIDs for anonymous classes compiled using 1.6 VMs

Chris Dennis <[email protected]>
Newsgroups gmane.comp.java.objectweb.asm
Message-ID <[email protected]>
Hi All,

Feeding the attached class (when compiled under 1.6) through an  
adapter chain containing just the SerialVersionUIDAdder (and then  
dumping) generates the following output:

==================================
// class version 50.0 (50)
// access flags 48
final class Test$1 extends Test {

   // compiled from: Test.java
   OUTERCLASS Test null
   // access flags 8
   static INNERCLASS Test$1 null null

   // access flags 0
   <init>()V
    L0
     LINENUMBER 4 L0
     ALOAD 0
     INVOKESPECIAL Test.<init> ()V
     RETURN
     MAXSTACK = 1
     MAXLOCALS = 1

   // access flags 24
   final static J serialVersionUID = -76581618747421564
}
==================================

The calculated serialVersionUID is different from the value serialver  
reports:

Test$1: static final long serialVersionUID = 7846639601304304217L;

The value serialver reports is the same as on 1.4 and 1.5 JVMs, which  
ASM correctly matches in those VMs. The only difference between the  
compiled classes are the access flags of the class: 1.5 and 1.4 report  
SUPER (0x0020), 1.6 reports SUPER|FINAL (0x0030) (and the class file  
version, obviously).

I believe this is because the change introduced previously to fix  
serialVersionUID values for nested classes was incorrect.  Inspecting  
the Sun 1.6 Hotspot sources reveals this code:

==================================
hotspot/src/share/vm/oops/instaceKlass.cpp::compute_modifier_flags(...)
(loop over InnerClass attribute list omitted)

int ioff = inner_class_list_h->ushort_at(i +  
instanceKlass::inner_class_inner_class_info_offset);

// Inner class attribute can be zero, skip it.
// Strange but true: JVM spec. allows null inner class refs.
if (ioff == 0) continue;

// only look at classes that are already loaded
// since we are looking for the flags for our self.
symbolOop inner_name = ik->constants()->klass_name_at(ioff);
if ((ik->name() == inner_name)) {
   // This is really a member class.
   access = inner_class_list_h->ushort_at(i +  
instanceKlass::inner_class_access_flags_offset);
   break;
}
==================================

As you can see the condition under which the inner class access flags  
are used is just that the class name of the inner class attribute  
(class name in the constant pool @ inner_class_info_offset - saved to  
inner_name) matches the class name of the class being inspected (ik- 
 >name()).  It follows therefore that the visitInnerClass method of  
SerialVersionUIDAdder should be:

==================================
public void visitInnerClass(final String aname, final String  
outerName, final String innerName, final int attr_access) {
     if ((name != null) && name.equals(aname)) {
         this.access = attr_access;
     }
     super.visitInnerClass(aname, outerName, innerName, attr_access);
}
==================================

The previous check will fail for anonymous inner classes since they  
have null constant pool references for outer_class_info and  
inner_name.  This means anonymous classes ended up using the modifiers  
of the class (which gained the FINAL flag in 1.6) thus making their  
calculated serialVersionUID values incorrect on 1.6.  I've attached a  
patch that solves this, and tested it with both the testcase for this  
bug, and also the original testcase that Simon Goldsmith provided  
(Frob.java) and everything works correctly under all JVM versions.

Thanks,

Chris

P.S. I've also filed this as a bug on the bug tracker.
Test.java (application/octet-stream, 139 B) - not displayed
inner_class_svuid.patch (application/octet-stream, 1.1 KB)
Index: src/org/objectweb/asm/commons/SerialVersionUIDAdder.java
===================================================================
--- src/org/objectweb/asm/commons/SerialVersionUIDAdder.java	(revision 1410)
+++ src/org/objectweb/asm/commons/SerialVersionUIDAdder.java	(working copy)
@@ -286,16 +286,8 @@
      * the class file in favor of the access bits InnerClass attribute.
      */
     public void visitInnerClass(final String aname, final String outerName, final String innerName, final int attr_access) {
-        if (outerName != null && innerName != null && name != null) {
-            int len = name.length();
-            int ilen = innerName.length();
-            int olen = outerName.length();
-            if (len == olen + 1 + ilen 
-                    && this.name.startsWith(outerName) && this.name.endsWith(innerName) 
-                    && this.name.charAt(olen) == '$') 
-            {
-                this.access = attr_access; 
-            }
+        if ((name != null) && name.equals(aname)) {
+	    this.access = attr_access;
         }
         super.visitInnerClass(aname, outerName, innerName, attr_access);
     }
message-footer.txt (text/plain, 238 B)
-- 
You receive this message as a subscriber of the [email protected] mailing list.
To unsubscribe: mailto:[email protected]
For general help: mailto:[email protected]?subject=help
OW2 mailing lists service home page: http://www.ow2.org/wws
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.