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

我一直在从各种文件中读取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字节的字符串文字?

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

查看JDK-4309152 : # Compiler silently generates bytecode that exceeds VM limits

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.

  1. 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限制,则编译器的标准行为是生成编译器错误.

转载注明原文:超过65535字节的Java字符串文字的字节码 - 代码日志