超过65535字节的Java字符串文字的字节码

4
我一直在阅读各种文件的Java字节码,以帮助我理解需要与第三方库集成的项目的.class文件,因为该库没有源代码且文档质量不佳。
出于自己的兴趣,我运行了Apache BCEL库通过我的Maven仓库,以查看更稀有的类和方法属性,例如类型注释是如何使用的以及原因。
我遇到了一个特定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。从视觉上查看文件,我可以看到所讨论的常量在位置0x149BD结束,这使得字符串的实际长度为0x1480E(基本上是位置0x1AC处的前三个字节)。当然,这是不可能的,因为JVM类文件规范具有最大长度为0xFFFF或65535的Utf8常量。该classfile相当老 - 版本46或Java 1.2。
我已经详细研究了规范,并尝试了不同的可能实现(更严格和更宽松),以尝试解析此常量,但它要么无法解析,要么会破坏其他有效的Utf8常量的读取。
那么我的问题是,我错过了什么,还是编译器出了问题,如果是这样的话,我的第二个问题是这种情况如何发生 - 编译器往往会经过相对彻底的检查。最后,Java编译器通常如何管理长度超过65535字节的字符串文字?

1
看起来编译器无法处理太大的常量:https://dev59.com/_GTWa4cB1Zd3GeqPClwf - user158037
鉴于OP提供的第一个错误 - 我也相信当前编译器中字符串字面量是有限制的。但是在这种情况下,甚至在创建类文件之前就已经存在了。这是一个已经编译好的类文件,因此还有其他问题。 - Mr Rho
1个回答

4
由于您提到“classfile非常古老 - 版本46或Java 1.2”,因此很可能是由于编译器在超出限制时未拒绝代码而导致的classfile损坏。
请参见JDK-4309152:# Compiler silently generates bytecode that exceeds VM limits
引用:

编译器不能正确地强制执行各种classfile组件的数量或大小的某些限制。这导致代码似乎可以成功编译,但在验证期间运行时失败。

最初报告了这些问题作为单独的错误,现在已经被关闭并列为此错误的重复项。每个项目都包含了原始错误编号。

...

  1. UTF-8编码字符串有64k的限制。(4071592)
据报道,该错误已经在1.3.1_10中得到修复,因此它符合时间范围。
请注意,所引用的 bug #4071592 是指在 1.2.0 及之前版本中尝试写入过大字符串时抛出 UTFDataFormatException,但是 #4303354 报告说在 1.3.0 中会生成无效字符串而不发出警告。因此,如果有问题的类文件是由 javac 生成的,则必须是在版本 1.3.01.3.1_10 之间使用 -target 1.2 生成的。
自修复以来,编译器的标准行为是如果某个结构超过类文件/JVM限制,则生成编译器错误。

我们还处于计算机科学的领域中吗,还是已经被视为考古学了? - GhostCat
@GhostCat 有时候两种情况都有可能发生。了解编译器只是软件,因此可能它们也会有bug,这可能会有所帮助。对我来说,感觉就像昨天一样 ;^) - Holger
@Holger,你干得太棒了。我在谷歌上的功夫远不如你。 - Mr Rho

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接