我一直在从各种文件中读取Java字节码,以帮助我理解项目的.class文件,我需要与没有源代码和可用文档很差的第三方库集成。
为了我自己的娱乐,我通过我的maven存储库运行Apache BCEL库,以查看使用类型注释等稀有类和方法属性的位置以及原因。
我偶然发现了一个特定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
)是一个长度为0x01
的Utf8常量(tag = 0x480E
)。向前移动文件中的数量确实在该位置有一个字节0x3C
。在视觉上查看文件,我可以看到有问题的常量在位置0x149BD
结束,这使得字符串0x1480E
的实际长度(实际上是位置0x1AC
的前三个字节)。根据JVM类文件规范,这当然是不可能的,对于Utf8常量,该规范的最大长度为0xFFFF
或65535。类文件很旧 - 版本46或Java 1.2。
我仔细考虑了规范并尝试了不同的可能实现(更少和更严格)来尝试解析这个常量,但是要么无法解析它,要么它会破坏其他有效Utf8常量的读取。
我的问题是,如果我错过了某些内容,或者是编译器错误,在这种情况下,我的第二个问题是如何首先发生这种情况 - 编译器往往会进行相对彻底的检查。最后,Java编译器通常如何管理长度超过65535 bytes 的字符串文字?
答案 0 :(得分:3)
由于您声明“类文件很旧 - 版本46或Java 1.2”,因此当编译器在超出限制时不会拒绝代码时,确实可能会破坏类文件。
请参阅JDK-4309152 : # Compiler silently generates bytecode that exceeds VM limits:
编译器未对数字或大小正确实施某些限制 各种类文件组件。这导致代码似乎编译 成功,但在验证期间在运行时失败。
这些最初被报告为单独的错误,现已关闭 作为这个的重复。每个都包含原始的错误号 项目如下。
...
- UTF-8编码字符串有64k限制。 (4071592)
醇>
报告此错误已修复为1.3.1_10
,因此符合时间范围。
请注意,引用的错误#4071592是指在UTFDataFormatException
及更早版本中尝试编写过大字符串时抛出1.2.0
,但#4303354报告在1.3.0
中无提示生成无效字符串{1}}。因此,如果有问题的类文件是由javac
生成的,那么它必须介于版本1.3.0
和1.3.1_10
之间-target 1.2
。
自修复以来,编译器的标准行为是在某个构造超出类文件/ JVM限制时生成编译器错误。