有一次编译,当引用了外部提供的 jar 包时,提示错误的类文件,报错类似这样。
排查问题
根据直观的提示,“错误的类文件”,说明可能是 class 文件损坏。所以检查 class 文件,发现可以反编译,并且这个 jar 包可以正常被执行。这缩小了排查范围,就是编译出错,执行没问题。
注意到,外部提供的 jar 包可能是用 eclipse 编译的。将编译器从 javac 换成 eclipse 编译器 ECJ
此时,使用 javac 不能编译的代码,使用 ECJ 可以编译!这样我们就能准确描述问题了,这是 ECJ 和 javac 的不兼容问题。
使用 ECJ 编译下面这个 java 文件,输出 MyEunm.class
文件后,在新的代码里引用 MyEnum
, 然后用 javac 编译新的代码,就会报错!
分析原因
使用 jclasslib 比较 ECJ 与 javac 输出的 MyEunm.class
文件,发现 ECJ 的 class 文件,相比旧版本的 javac,缺少了 Signature attr。说明确实是 ECJ 编译的文件,和 javac 的编译出的文件有区别。
进一步排查
因为 javac 本质上还是一个 java 程序,我们可以直接在 IDEA 中 debug javac。
最终,在代码中,确定:javac 在解析 EJC 编译的 class 时,会发现数组越界的现象。
上报给 JDT
因为这是 ECJ 和 javc 的不兼容问题,所以上报给 JDT 是合适的。我在 github 上提交了一个 issue ECJ#205,并附上了我的发现。 而 JDT 的维护者分析,ECJ 的行为符合 JVMS 规范,这是 javac 的一个错误!
在 JDT 和 OpenJDK 进行邮件沟通后
OpenJDK 新建了一个 issue,这是一个 Bug,并认为这不符合 JVMS 4.7.18 和 4.7.19。
在已经提交的代码中,我们可以发现,OpenJDK 新建了一个 method adjustParameterAnnotations
,在 setParameters
前调用,而后面这个函数,正是之前我们断点定位到的位置。
我们将在 24 版本,看到这个 Bug 被修复。