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 itsACC_FINAL
flag set.
Version 9 of the JVM spec has similar words to say:
If the
ACC_INTERFACE
flag is set, theACC_ABSTRACT
flag must also be set, and theACC_FINAL
,ACC_SUPER
,ACC_ENUM
, andACC_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?
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;
}