我一直在从各种文件中读取 Java 字节码,以帮助我理解一个项目的 .class 文件,在该项目中我需要与没有源代码和可用文档不足的第 3 方库集成。
为了我自己的乐趣,我通过我的 maven 存储库运行了 Apache BCEL 库,以查看在哪里使用了较少见的类和方法属性(例如类型注释)以及原因。
我偶然发现了一个特定 jar 的问题,该 jar 无法解码常量字段之一 - 特别是 CONSTANT_Utf8_info。库是icu4j-2.6.1.jar (com.ibm.icu:icu4j)
,特别是LocaleElements_zh__PINYIN.class
文件。Apache BCEL 失败(以及我自己在符合 JVMS 版本 8 和 9 的快速字节码阅读器上的尝试)偶然发现了同样的问题,他们误读了这个常量,然后读取了下一个字节,该字节被评估为不正确的常量标签 (0x3C/60) .
快速检查我是否可以在 IDE 中使用该类失败(无法解析符号)。使用十六进制编辑器研究实际字节码,显示该偏移量 ( 0x1AC
) 处的常量是长度为 的 Utf8 常量 (tag= 0x01
) 0x480E
。向前移动文件中的该数量确实0x3C
在该位置有一个字节。直观地查看文件,我可以看到有问题的常量在 location 结束,0x149BD
这使得字符串的实际长度0x1480E
(本质上是 location 的前三个字节0x1AC
)。根据 JVM 类文件规范,这当然不可能,因为0xFFFF
Utf8 常量的最大长度为 65535 或 65535。类文件相当旧 - 版本 46 或 Java 1.2。
我仔细研究了规范并尝试了不同的可能实现(越来越严格)来尝试解析这个常量,但它要么无法解析它,要么破坏了其他有效 Utf8 常量的读取。
那么我的问题是,我是否遗漏了什么,或者是编译器错误,在这种情况下,我的第二个问题是这首先是如何发生的 - 编译器往往经过相对彻底的检查。最后,Java 编译器通常如何管理长度超过 65535字节的字符串文字?