Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6228074

In light of conditional compilation, javac generates invalid "checkcast"

XMLWordPrintable

    • generic, x86
    • generic, linux

      FULL PRODUCT VERSION :
      java version "1.5.0"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-b64)
      Java HotSpot(TM) Client VM (build 1.5.0-b64, mixed mode, sharing)

      Bug is NOT present in the following:
      java version "1.3.1_07"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1_07-b02)
      Java HotSpot(TM) Client VM (build 1.3.1_07-b02, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows XP [Version 5.1.2600]

      A DESCRIPTION OF THE PROBLEM :
      This problems seems to have been introduced in JDK 1.4.x; it is not visible in the JDK 1.3.1 or JDK 1.1.8 compiler.

      The javac has a neat "conditional compilation" feature, whereby code that is known to be dead at compile-time (e.g., code within an if (false) block) is not generated. Apparently this even includes whether class files are generated for inner classes. For example the following code:

      class A
      {
          static final boolean FLAG = false;
          static final B obj = !FLAG ? null : new B() {};
      }
      class B
      {
      }

      For this code, javac will NOT generate a class file for the anonymous inner class. However, the JDK 1.4-1.5 javac end up generating a reference to the class in the static initializer as part of a "checkcast" instruction. The JDK 1.3 javac did not generate this "checkcast" instruction, and hence no reference.

      Here is the JDK 1.5 static initializer:

      static {};
        Code:
         0: aconst_null
         1: checkcast #2; //class A$1
         4: putstatic #3; //Field obj:LB;
         7: return

      And here is the JDK 1.3.1 static initializer:

      Method static {}
         0 aconst_null
         1 putstatic #2 <Field B obj>
         4 return

      According to the JVM spec, the "checkcast" instruction must resolve the class reference. According to 5.4.3.1 and 5.3 of the JVM spec this should result in the loading of the referenced class by the classloader (which should fail). The VM fails as expected here generating a NoClassDefFoundError (as long as something other than "-source 1.5" is specified).

      To summarize, the problem is that javac generates a reference to a non-existent class such that the generated code cannot be executed by the VM.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Create a file Bug.java with the following contents:

      public class Bug
      {
          public static void main(String[] args)
          {
              System.out.println(A.class);
          }
      }
      class A
      {
          static final boolean FLAG = false;
          static final B obj = !FLAG ? null : new B() {};
      }
      class B
      {
      }
      /* EOF*/

      Now compile it with the JDK 1.5 compiler like so:

      > javac -target 1.1 -source 1.3 Bug.java

      And disassemble A like so:

      > javap -c A

      The Bug class can be run like so:

      > java Bug

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      When compiling the above program with with JDK 1.3.1 like so:

      > javac -target 1.1 Bug.java

      And disassembling like so:

      > javap -c A

      I get this output:

      Compiled from Bug.java
      class A extends java.lang.Object {
          static final boolean FLAG;
          static final B obj;
          A();
          static {};
      }

      Method A()
         0 aload_0
         1 invokespecial #1 <Method java.lang.Object()>
         4 return

      Method static {}
         0 aconst_null
         1 putstatic #2 <Field B obj>
         4 return

      Note that the static initializer does not have a "checkstatic" reference to A$1.

      When running "java Bug" I would expect the following output:

      class A

      However, given the generated code I completely expect the error that is generated:

      Exception in thread "main" java.lang.NoClassDefFoundError: A$1
              at java.lang.Class.forName0(Native Method)
              at java.lang.Class.forName(Class.java:164)
              at Bug.class$(Bug.java:5)
              at Bug.main(Bug.java:5)
      ACTUAL -
      Actual results with JDK 1.5:

      Compiled from "Bug.java"
      class A extends java.lang.Object{
      static final boolean FLAG;

      static final B obj;

      A();
        Code:
         0: aload_0
         1: invokespecial #1; //Method java/lang/Object."<init>":()V
         4: return

      static {};
        Code:
         0: aconst_null
         1: checkcast #2; //class A$1
         4: putstatic #3; //Field obj:LB;
         7: return

      }

      Note the "checkstatic" instruction that references A$1. However no such A$1 class is created (as expected).

      Given the generated code, the expected NoClassDefFoundError is generated.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      public class Bug
      {
          public static void main(String[] args)
          {
              System.out.println(A.class);
          }
      }
      class A
      {
          static final boolean FLAG = false;
          static final B obj = !FLAG ? null : new B() {};
      }
      class B
      {
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      The workaround I have found (besides using an older javac and not using conditional compilation in this way) is to insert an explicit cast. I.e., instead of the following code:

          static final B obj = !FLAG ? null : new B() {};

      The workaround is to do the following:

          static final B obj = !FLAG ? null : ((B)(new B() {}));

      This will generate the following static initializer with JDK 1.5 javac:

      static {};
        Code:
         0: aconst_null
         1: checkcast #2; //class B
         4: putstatic #3; //Field obj:LB;
         7: return
      ###@###.### 2005-2-11 02:42:44 GMT

            sundar Sundararajan Athijegannathan
            gmanwanisunw Girish Manwani (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: