Search code examples
javajvmbytecode.class-filejvm-bytecode

Why interface Member from Java 1.0.2 does not have ACC_ABSTRACT set?


I've written a simple Java bytecode parser to do some experimentation and recently it failed in an unexpected place. While reading java/lang/reflect/Member.java from Java 1.1.8.16's rt.jar, my parser got mad because Member starts out like so (note the missing ACC_ABSTRACT flag):

Classfile Member.class
  Last modified Aug 8, 2002; size 350 bytes
  MD5 checksum 9a1aaec8e70e9a2ff9d63331cb0ea34e
  Compiled from "Member.java"
public interface java.lang.reflect.Member
  minor version: 3
  major version: 45
  flags: (0x0201) ACC_PUBLIC, ACC_INTERFACE
...

The version from Java 1.2.2.17 corrects this and has flags set to 0x0601 (ACC_ABSTRACT | ACC_INTERFACE | ACC_PUBLIC).

The earliest JVM specification I can find (purportedly 1.0.2) has this to say (§4.1, p. 86, emphasis added):

An interface is implicitly abstract (§2.13.1); its ACC_ABSTRACT flag must be set. An interface cannot be final; its implementation could never be completed (§2.13.1) if it were, so it could not have its ACC_FINAL flag set.

Version 9 of the JVM spec has similar words to say:

If the ACC_INTERFACE flag is set, the ACC_ABSTRACT flag must also be set, and the ACC_FINAL, ACC_SUPER, ACC_ENUM, and ACC_MODULE flags set must not be set.

Does the Oracle/Sun JVM enforce this requirement that "must be" so? If so, since when? And if not, why does the JVM specification bother to pretend it is required?


Solution

  • This was a bug JDK-4059153: javac did not set ACC_ABSTRACT for interfaces.

    The bug was fixed in 1.2, but since there were already many classes compiled with this bug, JVM got a workaround to add ACC_ABSTRACT automatically for all classes with ACC_INTERFACE. This worked until Java 6, when it was finally decided to strictly follow the specification for newer class files. However, for backward compatibility with older versions of class files, the workaround still exists up until now, see classFileParser.cpp:

        if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) {
          // Set abstract bit for old class files for backward compatibility
          flags |= JVM_ACC_ABSTRACT;
        }