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

javac emits incorrect invokestatic call

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Duplicate
    • Icon: P4 P4
    • None
    • 1.3.0
    • tools
    • generic
    • generic



      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)
      ======================================================================

            gafter Neal Gafter (Inactive)
            bonealsunw Bret O'neal (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: