1

来自Java 虚拟机规范

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

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

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

u1明显u2

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

该规范建议这样做:

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

大于 的值会发生什么Integer.MAX_VALUE?这个建议是否暗示所有类型的值u4都小于或等于Integer.MAX_VALUE

我想出了这个主意:

  • u4: 读取readUnsignedInt,存储在一个long

不幸的是,没有这样的方法。但这不是问题,因为您可以轻松编写自己的:

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

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

  1. 代码属性

    Code_attribute {
    ...
    u4 code_length;
    u1 代码[code_length];
    ...
    }

    为什么code_length不是类型u2后来它说

    code_length项目的值必须小于 65536。

  2. SourceDebugExtension属性

    SourceDebugExtension_attribute {
    ...
    u4 attribute_length;
    u1 debug_extension[attribute_length];
    ...请注意,该
    数组表示的字符串可能比用 class 的实例表示的字符串长
    debug_extensionString

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

4

2 回答 2

1
  1. 如果需要,可以轻松解除 64K 代码长度限制。
  2. 由于没有提到 u4 值不能超过 Integer.MAX_VALUE,因此必须假设 u4 值可以超过 Integer.MAX_VALUE。JVM 规范没有隐含任何内容。
于 2012-07-26T02:01:37.930 回答
0

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

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

因此,如果类文件的大小本质上被限制为最大有符号数int,即2³¹字节,则无需考虑类文件的一部分(如一个属性)大于该值的可能性。虽然您可以使用如此庞大的属性形成理论上正确的类文件,但即使 JVM 本身也不支持它。这种场景也没有现实生活的相关性。

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

如果文件小于Integer.MAX_VALUE或您通过 API 接收类文件,该 API 本质上将类文件限制为小于该值,例如通过传递数组 or ByteBuffer,您可以继续并认为每个u4大小值大于Integer.MAX_VALUE无效,因为它表示比类文件本身更大的大小。您不需要特殊的readUnsignedInt方法,因为int两者u4具有相同的大小,您只需正确解释该值即可整理出有效正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

对于早期版本,您可能会认为u4大于 的值Integer.MAX_VALUE在解释为时会显示为负数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
于 2017-01-09T18:06:35.593 回答