Name: boT120536 Date: 03/09/2001
java version "1.3.0_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0_01)
Java HotSpot(TM) Client VM (build 1.3.0_01, mixed mode)
Javac emits bytecode in violation of JLS2 12.4.1, 13.1, 13.4.11, and 15.12.1.
Referencing a static member should save in the compiled binary a reference to
the class qualifying the reference, which is not necessarily the class where the
static object was declared.
First, compile these three source files:
class A {
static void foo() {
System.out.println("A");
}
static {
System.out.println("Init. A");
}
}
class B extends A {
static {
System.out.println("Init. B");
}
}
class Test {
public static void main(String[] args) {
B.foo();
}
}
At this point, the correct output is produced, but for the wrong reason:
Init. A
A
According to JLS 13.1:
"If the reference is of the form X.m, where X denotes a class or interface, then
the class or interface denoted by X is the qualifying type of the method
invocation." Therefore, the bytecode for Test.class should include an
invokestatic that references B.foo(), even though at runtime it will be A.foo()
that is executed. However, javac has emitted a class that references A.foo().
To demonstrate this bug, recompile just B to override A.foo().
class B extends A {
static void foo() {
System.out.println("B");
}
static {
System.out.println("Init. B");
}
}
Now, since Test.class should still refer to B.foo(), the correct output must
change to the following:
Init. A
Init. B
B
However, since javac emitted the wrong code in Test.class, the resulting output
is wrong:
Init. A
A
Contrast this behavior to the example in 13.4.5, where compiling to an inherited
instance method invocation, then modifying the class to declare that method,
correctly changes the output.
(Note that a clean compilation of all three original files with a compiler that
does not have this bug will produce yet another incorrect output:
Init. A
Init. B
A
This is due to a bug in the JDK 1.3 java virtual machine. According to JVMS
5.4.3.3, 5.5, and 6, the VM must not initialize a class during invokestatic
unless the class actually declares the method, but java wrongly initializes
class B when resolving that B.foo() refers to A.foo().)
For further proof that javac is in error, recompile both A and B as follows:
class A {}
class B extends A {
int foo() {return 1;}
}
Then compile just A back to its original state:
class A {
static void foo() {}
}
Now, according to 13.4.11, execution of Test must cause a linkage error, since
it should refer to B.class which has replaced the static method with an
incompatible instance method. But, compiling with javac has linked Test to
A.foo(), which still has the correct method signature, and the program executes
without exception.
(Review ID: 118535)
======================================================================
- duplicates
-
JDK-4393930 Default for -target should be 1.2 in Merlin
- Closed