3

我编写了一个简单的 Java 字节码解析器来做一些实验,最近它在一个意想不到的地方失败了。java/lang/reflect/Member.java从 Java 1.1.8.16读取时rt.jar,我的解析器发疯了,因为Member开始时是这样的(注意缺少的ACC_ABSTRACT标志):

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
...

Java 1.2.2.17 的版本更正了这一点,并将标志设置为0x0601( ACC_ABSTRACT | ACC_INTERFACE | ACC_PUBLIC)。

我能找到的最早的 JVM 规范(据称是 1.0.2)有这样的说法(§4.1,第 86 页,强调添加):

接口是隐式抽象的(§2.13.1);它ACC_ABSTRACT的标志必须被设置。接口不能是最终的;如果是这样,它的实现将永远无法完成(第 2.13.1 节),因此它不能ACC_FINAL设置其标志。

JVM 规范的第 9 版有类似的话要说

如果设置了标志ACC_INTERFACE,则ACC_ABSTRACT标志也必须设置,并且不能设置ACC_FINALACC_SUPERACC_ENUMACC_MODULE标志集。

Oracle/Sun JVM 是否强制执行“必须”这样的要求?如果有,从什么时候开始?如果不是,为什么 JVM 规范还要假装它是必需的?

4

2 回答 2

4

这是一个错误JDK-4059153:没有ACC_ABSTRACT为接口设置 javac。

该错误在 1.2 中已修复,但由于已经有许多使用此错误编译的类,JVM 有一个解决方法来ACC_ABSTRACT自动为所有使用ACC_INTERFACE. 这一直有效到 Java 6,当它最终决定严格遵循新类文件的规范时。但是,为了向后兼容旧版本的类文件,解决方法直到现在仍然存在,请参阅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;
    }
于 2017-12-18T17:28:55.040 回答
0

我不清楚为什么,但我通过创建以下类进行实验,用 Java 9 编译它们,javac然后手动编辑access_flags0x200in Foo; 然后手动循环minor_versionmajor_version浏览不同的 Java 版本,看看会发生什么:

interface Foo { }

class Bar implements Foo {
    public static void main(String[] args) {
        System.out.println("BAZ");
    }
}

结果:

╭──────╥───────┬───────┬─────╮
│ Java ║ minor │ major │ out │
╞══════╬═══════╪═══════╪═════╡
│  1.1 ║    03 │    2d │ BAZ │
│  1.2 ║     " │     " │ BAZ │
│  1.3 ║     " │    2e │ BAZ │
│  1.4 ║    00 │    2f │ BAZ │
│   5  ║    00 │    31 │ BAZ │
│   6  ║    00 │    32 │ err │
│   7  ║    00 │    33 │ err │
│   8  ║    00 │    34 │ err │
│   9  ║    00 │    35 │ err │
└──────╨───────┴───────┴─────┘

"err" 真正打印出来的地方是这样的:

Error: LinkageError occurred while loading main class Bar
    java.lang.ClassFormatError: Illegal class modifiers in class Foo: 0x200

所以我猜他们终于开始在 Java 6 中强制执行它了。

仍然不确定为什么。

于 2017-12-18T15:25:00.580 回答