-
Bug
-
Resolution: Duplicate
-
P4
-
None
-
1.4.2, 5.0
-
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
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
- duplicates
-
JDK-6241465 Invalid reference to anonymous class in ?: operator with cast and a null value.
- Closed
-
JDK-6587674 NoClassdefFound when anonymously extending a class
- Closed