JDK-6378717 : bad constructor signature in inner class file
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 6
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: linux
  • CPU: x86
  • Submitted: 2006-01-30
  • Updated: 2010-05-08
  • Resolved: 2006-01-31
Description
FULL PRODUCT VERSION :
java version "1.6.0-rc"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.6.0-rc-b65)
Java HotSpot(TM) Client VM (build 1.6.0-rc-b65, mixed mode, sharing)


ADDITIONAL OS VERSION INFORMATION :
Linux Everest 2.6.9-5.EL #1 Wed Jan 5 19:22:18 EST 2005 i686 i686 i386 GNU/Linux
Windows XP

A DESCRIPTION OF THE PROBLEM :
When we decompile the file Test$Request.class  associate with the code,

public abstract class Test {
    private final class Request {}
    private Coucou anoymous =
            new Coucou() {
                    protected Object initialValue() {
                    return new Request();
        }
    };
}
class Coucou {}

we find that the inner class request have a constructor with the wrong signature:
      Test$Request(Test, Test$1);
and 2 variables ( x0 and x1) are delared.

Javap and ASM find the same trouble.

 javap -verbose  Test\$Request


Compiled from "Test.java"
final class Test$Request extends java.lang.Object
  SourceFile: "Test.java"
  InnerClass:
   final #14= #4 of #28; //Request=class Test$Request of class Test
   final #17; //class Test$1
  minor version: 0
  major version: 50
  Constant pool:
const #1 = Method       #4.#25; //  Test$Request."<init>":(LTest;)V
const #2 = Field        #4.#26; //  Test$Request.this$0:LTest;
const #3 = Method       #5.#27; //  java/lang/Object."<init>":()V
const #4 = class        #29;    //  Test$Request
const #5 = class        #30;    //  java/lang/Object
const #6 = Asciz        this$0;
const #7 = Asciz        LTest;;
const #8 = Asciz        <init>;
const #9 = Asciz        (LTest;)V;
const #10 = Asciz       Code;
const #11 = Asciz       LineNumberTable;
const #12 = Asciz       LocalVariableTable;
const #13 = Asciz       this;
const #14 = Asciz       Request;
const #15 = Asciz       InnerClasses;
const #16 = Asciz       LTest$Request;;
const #17 = class       #31;    //  Test$1
const #18 = Asciz       ;
const #19 = Asciz       (LTest;LTest$1;)V;
const #20 = Asciz       x0;
const #21 = Asciz       x1;
const #22 = Asciz       LTest$1;;
const #23 = Asciz       SourceFile;
const #24 = Asciz       Test.java;
const #25 = NameAndType #8:#9;//  "<init>":(LTest;)V
const #26 = NameAndType #6:#7;//  this$0:LTest;
const #27 = NameAndType #8:#32;//  "<init>":()V
const #28 = class       #33;    //  Test
const #29 = Asciz       Test$Request;
const #30 = Asciz       java/lang/Object;
const #31 = Asciz       Test$1;
const #32 = Asciz       ()V;
const #33 = Asciz       Test;

{
final Test this$0;

Test$Request(Test, Test$1);
  Code:
   Stack=2, Locals=3, Args_size=3
   0:   aload_0
   1:   aload_1
   2:   invokespecial   #1; //Method "<init>":(LTest;)V
   5:   return
  LineNumberTable:
   line 19: 0

  LocalVariableTable:
   Start  Length  Slot  Name   Signature
   0      6      0    this       LTest$Request;
   0      6      1    x0       LTest;
   0      6      2    x1       LTest$1;


}


If we have this Test class:

   /public abstract class Test {
       private final class Request {}
   }/

The inner class Test$Request have one constructeur <init> with the signature (LTest;)V

Now, if we modified the class to

/    public abstract class Test {
       private final class Request {}
       private Coucou anoymous =
           new Coucou() {
                   protected Object initialValue() {
                   return new Request();
           }
       };
   }
   class Coucou {}/

The inner class Test$Request have two constructeurs            <init> with the signature (LTest;)V                             (A)
           <init> with the signature (LTest;LTest$1;)V              (B)

where Test$1 correspond to the anonymous class.

The constructeur (B) is superflous, and seem not to be used in the rest of the programm.

javap see only (B), and ASM see (A) and (B) 




REPRODUCIBILITY :
This bug can be reproduced always.

Comments
EVALUATION This is not a bug. The compiler is trying to solve an access problem. Since the inner class Test.Request is private its constructor is private. This can be seen if you use -private to javap: $ javap -private Test\$Request Compiled from "Test.java" final class Test$Request extends java.lang.Object{ final Test this$0; private Test$Request(Test); Test$Request(Test, Test$1); } However, the JVM will not allow the anonymous subclass of Coucou (Test$1) access to this private constructor. This is a fundamental difference between the JVM and the Java programming language when it comes to nested classes. The language allows nested classes to access private members of the enclosing class. Originally, when nested classes were added to the language, the solution to this problem was to make the constructor package private and would have looked like this: $ javap -private Test\$Request Compiled from "Test.java" final class Test$Request extends java.lang.Object{ final Test this$0; Test$Request(Test); } However, this can easily lead to problems where you can get access to the constructor when you shouldn't. To address this problem, the current solution was invented. The "real" constructor will remain private: private Test$Request(Test); However, other nested classes must be allowed to call this constructor. So an access constructor must be provided. However, this access constructor must be different from the "real" constructor. To solve this problem the compiler adds an extra parameter to the access constructor. The type of this extra parameter must be something unique that doesn't conflict with anything the user might have written. So an obvious solution is to add an anonymous class and use that as the type of the second parameter: Test$Request(Test, Test$1); However, the compiler is clever and reuses any anonymous class if one exists. If you change the example to not include an anonymous class, you will see that the compiler will create one: public abstract class Test { private final class Request {} private final class OtherRequest { Request test() { return new Request(); } } } If there is no access to the private constructor, the compiler doesn't need to generate any access constructor which explains the behavior of this example: public abstract class Test { private final class Request {} }
31-01-2006