我一直在从各种文件中读取Java字节码,以帮助我理解项目中的.class文件,在该项目中,我需要与没有源代码和可用文档不足的第三方库集成。

出于我自己的娱乐,我在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。直观地查看文件,我可以看到所讨论的常量在位置0x149BD结束,该位置使字符串0x1480E的实际长度(实际上是位置0x1AC的前三个字节)。根据JVM类文件规范,这当然是不可能的,对于Utf8常量,其最大长度为0xFFFF或65535。该类文件非常旧-版本46或Java 1.2。

我仔细研究了该规范,并尝试了其他可能的实现方式(越来越严格)来解析此常量,但它无法解析该常量,或者破坏了对其他有效Utf8常量的读取。

然后我的问题是,我错过了什么,还是编译器错误?在这种情况下,我的第二个问题是,这怎么可能首先发生-编译器往往会进行相对彻底的检查。最后,Java编译器通常如何管理长度超过65535字节的字符串文字?

看起来编译器只是不处理太大的常量:stackoverflow.com/questions/10798769/

当然,考虑到OP给的第一个错误-我也相信字符串文字仅限于当前的编译器中。 但是,在这种情况下,甚至是在创建类文件之前。 这是一个已经编译的类文件,因此还有其他问题。

既然您说过"类文件已经很旧了-版本46或Java 1.2",则确实有可能由于当时的编译器在超出限制时不拒绝代码而简单地破坏了类文件。

请参阅JDK-4309152:#编译器静默生成超出VM限制的字节码:

The compiler does not properly enforce certain limits on the number or size
of various classfile components. This results in code that appears to compile
successfully, but fails at runtime during verification.
These were originally reported as separate bugs, which have now been closed
as duplicates of this one. The original bug numbers are included with each
item below.
…
There is a 64k limit on UTF-8 encoded strings. (4071592)

据报此错误已为1.3.1_10修复,因此适合时间范围。

请注意,所引用的错误#4071592是指尝试在1.2.0和更早版本中写入过大的字符串时抛出UTFDataFormatException的问题,但是#4303354报告说,在1.3.0中静默生成了无效的字符串。 因此,如果有问题的类文件是由javac生成的,则它必须已经在版本1.3.0和1.3.1_10与-target 1.2之间。

自修复以来,编译器的标准行为是,如果某些构造超出类文件/ JVM限制,则生成编译器错误。

我们还处于计算机科学领域,还是已经被认为是考古学?

@GhostCat有时,两者都是。 意识到编译器只是软件可能会有所帮助,因此,它们也可能存在错误。 对我来说,就像昨天一样; ^)

真棒在那挖@Holger。 我的谷歌福是次等的。