【问题标题】:How to handle the unsigned types (especially u4) of a Java class file in a Java program?如何在 Java 程序中处理 Java 类文件的无符号类型(尤其是 u4)?
【发布时间】:2012-07-26 01:08:13
【问题描述】:

来自Java Virtual Machine specification

class 文件由 8 位字节流组成。所有 16 位、32 位和 64 位量都是通过分别读取两个、四个和八个连续的 8 位字节来构造的。多字节数据项始终以大端顺序存储,高字节在先。在 Java 平台中,接口 java.io.DataInput 和 java.io.DataOutput 以及 java.io.DataInputStream 和 java.io.DataOutputStream 等类都支持这种格式。

本章定义了自己的一组数据类型,表示class 文件数据:u1u2u4 类型分别表示无符号的一、二或四字节数量。在Java平台中,这些类型可以通过接口java.io.DataInputreadUnsignedBytereadUnsignedShortreadInt等方法读取。

除了提到“64 位数量”(没有u8long and double 分为两个u4 项)之外,我不明白如何处理u4 类型。

对于u1u2,很明显:

  • u1:使用readUnsignedByte 读取,存储在int
  • u2:使用readUnsignedShort 读取,存储在int

规范建议这样做:

  • u4:使用readInt 读取,存储在int (?)

大于Integer.MAX_VALUE 的值会怎样?这个建议是否暗指u4 类型的所有值都小于或等于Integer.MAX_VALUE

我想出了这个主意:

  • u4:使用readUnsignedInt 读取,存储在long

不幸的是,there is no such method。但这不是问题,因为您可以轻松编写自己的代码:

public long readUnsignedInt() throws IOException {
    return readInt() & 0xFFFFFFFFL;
}

所以,这里有两个值得怀疑的地方:

  1. Code attribute

    代码属性{
    ...
    u4 code_length;
    u1 代码[code_length];
    ...
    }

    为什么code_length 不是u2 类型? Later it says:

    code_length 项的值必须小于 65536。

  2. SourceDebugExtension attribute:

    SourceDebugExtension_attribute {
    ...
    u4 属性长度;
    u1 debug_extension[attribute_length];
    }
    ...
    请注意,debug_extension 数组表示的字符串可能比 String 类的实例可以表示的字符串长。

    为什么? u4 的值确实可以超过Integer.MAX_VALUE(因为我认为这是String 实例的最大长度)?

【问题讨论】:

    标签: java jvm bytecode unsigned


    【解决方案1】:
    1. 在需要时轻松解除 64K 代码长度限制。
    2. 既然没有提到 u4 值不能超过 Integer.MAX_VALUE,那么我们必须假设 u4 值可以超过 Integer.MAX_VALUE。 JVM 规范没有隐含任何内容。

    【讨论】:

      【解决方案2】:

      如果你想有效地处理类文件,你不应该把太多的资源浪费在纯粹的假设上。正如您自己注意到的,代码数组的大小指定为u4,但实际支持的值仅限于u2。同样,所有其他 u4 大小值都受到隐式限制,如果您认为唯一官方支持的将类文件放入 JVM 的方法是基于 on an arrayon a ByteBuffer,两者都被限制为带符号的 int 表示它们的总大小。

      即使有办法将更大的类文件放入 JVM,也有其他部分,like the Instrumentation API,期待将类转换回普通数组的可能性。即使未来的 JVM 真正支持更大的类文件,通过使用新缓冲区类型的替代方案来扩充所有 API,您今天编译的应用程序也仅限于今天的 API 和缓冲区类型。

      因此,如果类文件的 total 大小本质上限制为最大有符号 int,即 2³¹ 字节,则无需考虑 部分的可能性类文件的,就像一个属性,比那个大。虽然你可以用如此庞大的属性构建一个理论上正确的类文件,但即使 JVM 本身也不支持它。此类场景也与现实生活无关。

      所以如果问题不是如何处理它们,问题是如何正确拒绝它们。如果您要处理一个文件,它确实可能有超过Integer.MAX_VALUE 字节,如果您要将文件读入缓冲区以进行进一步处理,无论如何您都必须考虑这一点。然后,在做任何其他事情之前检查大小并抛出UnsupportedOperationException 是合适的。在 32 位 JVM 上,即使抛出 OutOfMemoryError 也是合适的,因为任何真正尝试缓冲该文件的内容都会以这种方式结束。

      如果文件小于Integer.MAX_VALUE 或者您通过 API 接收类文件,该 API 本质上将类文件限制为小于该值,例如通过传递一个数组或ByteBuffer,您可以继续并将每个大于Integer.MAX_VALUEu4 大小值视为无效,因为它表示的大小大于类文件本身。您不需要特殊的readUnsignedInt 方法,因为intu4 都具有相同的大小,您只需解释正确的值以整理出有效正数之外的值int 范围。

      使用 Java 8,这尤其容易:

      int codeSize=bytebuffer.getInt();
      if(Integer.compareUnsigned(codeSize, 65536)>0)
          throw new IllegalArgumentException(
              "invalid code size "+Integer.toUnsignedString(codeSize));
      // carry on using the int value ordinarily
      

      对于早期版本,您可能会认为大于Integer.MAX_VALUEu4 值在解释为int 时会显示为负数:

      int codeSize=bytebuffer.getInt();
      if(codeSize<0 || codeSize>65536)
          throw new IllegalArgumentException("invalid code size "+(codeSize&0xFFFFFFFFL));
      // carry on using the int value ordinarily
      

      同样,处理其他 u4 大小值:

      int size=bytebuffer.getInt();
      // ByteBuffer can't be bigger than Integer.MAX_VALUE bytes:
      if(size<0) throw new IllegalArgumentException(
          "truncated class file (attribute size "+(size&0xFFFFFFFFL)+')');
      // carry on using the int value ordinarily
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-03-13
        • 2013-05-30
        • 2011-07-05
        • 2018-09-29
        • 2014-02-01
        • 2015-05-28
        • 1970-01-01
        相关资源
        最近更新 更多