【问题标题】:Bytecode for Java string literals longer than 65535 bytes长度超过 65535 字节的 Java 字符串文字的字节码
【发布时间】:2018-02-09 21:44:14
【问题描述】:

我一直在从各种文件中读取 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) 是长度为 0x480E 的 Utf8 常量 (tag=0x01)。向前移动文件中的该数量确实在该位置有一个字节0x3C。直观地查看文件,我可以看到有问题的常量在位置0x149BD 结束,这使得字符串的实际长度为0x1480E(本质上是位置0x1AC 的前三个字节)。根据 JVM 类文件规范,这当然是不可能的,对于 Utf8 常量,最大长度为 0xFFFF 或 65535。类文件相当旧 - 版本 46 或 Java 1.2。

我仔细研究了规范并尝试了不同的可能实现(更严格和更严格)来尝试解析这个常量,但它要么无法解析它,要么会破坏其他有效 Utf8 常量的读取。

然后我的问题是,我是否遗漏了某些东西,或者是编译器错误,在这种情况下,我的第二个问题是,这首先是如何发生的 - 编译器往往经过相对彻底的检查。最后,Java 编译器通常如何管理长度超过 65535 个字节的字符串文字?

【问题讨论】:

  • 编译器似乎无法处理太大的常量:stackoverflow.com/questions/10798769/…
  • 授予,鉴于 OP 给出的第一个错误 - 我也相信字符串 literals 仅限于当前编译器中的字符串。但是在这种情况下,它甚至在创建类文件之前。这是一个已经编译的类文件,因此还有其他问题。

标签: java jvm-bytecode


【解决方案1】:

既然你说“类文件很旧 - 版本 46 或 Java 1.2”,确实有可能是因为当时的编译器在超出限制时没有拒绝代码,所以类文件被简单地破坏了。

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

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

这些最初是作为单独的错误报告的,现已关闭 作为这个的副本。每个都包含原始错误编号 下面的项目。

  1. UTF-8 编码的字符串有 64k 的限制。 (4071592)

据报道此错误已针对1.3.1_10 进行了修复,因此它符合时间框架。

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

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

【讨论】:

  • 我们是否仍处于计算机科学领域,或者这已经被认为是考古学?
  • @GhostCat 有时,两者兼而有之。意识到编译器只是软件可能会有所帮助,因此它们也可能存在错误。对我来说,感觉就像昨天一样;^)
  • 很棒的挖掘@Holger。我的 google-fu 比较差。
猜你喜欢
  • 1970-01-01
  • 2015-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多