62

传统观点认为您只能抛出Throwable在 Java 中扩展的对象,但是否可以禁用字节码验证器并让 Java 编译和运行抛出任意对象甚至原语的代码?

我查看了 JVM athrow,它会弹出操作数堆栈上的第一个 objref;Throwable但它会在运行时检查所述引用是否指向 a吗?

4

3 回答 3

75

这取决于您的 JVM 实现。根据 Java VM 规范,如果对象不是Throwable.

objectref必须是引用类型,并且必须引用作为 Throwable 类或 Throwable 子类的实例的对象。

第 6.1 节“‘必须’的含义”中

如果在运行时不满足指令描述中的某些约束(“必须”或“不得”),则 Java 虚拟机的行为是未定义的。

我使用Jasmin 汇编器编写了一个测试程序,它相当于throw new Object(). Java HotSpot Server VM 抛出VerifyError

# cat Athrow.j 
.source Athrow.j
.class public Athrow
.super java/lang/Object

.method public <init>()V
    aload_0
    invokenonvirtual java/lang/Object/<init>()V
    return
.end method

.method public static main([Ljava/lang/String;)V
    .limit stack 2

    new java/lang/Object
    dup
    invokenonvirtual java/lang/Object/<init>()V
    athrow

    return
.end method

# java -jar jasmin.jar Athrow.j 
Generated: Athrow.class

# java Athrow
Exception in thread "main" java.lang.VerifyError: (class: Athrow, method: main signature: ([Ljava/lang/String;)V) Can only throw Throwable objects

禁用字节码验证器允许athrow执行,当 JVM 尝试打印异常的详细信息时,它似乎崩溃了。比较这两个程序,第一个抛出一个Exception,第二个是上面的测试程序抛出一个Object。注意它是如何在打印输出中间退出的:

# java -Xverify:none examples/Uncaught
Exception in thread "main" java.lang.Exception
        at examples.Uncaught.main(Uncaught.j)
# java -Xverify:none Athrow
Exception in thread "main" #

当然,禁用字节码验证器是危险的。编写 VM 本身以假定已执行字节码验证,因此不必对指令操作数进行类型检查。注意:绕过字节码验证时调用的未定义行为很像 C 程序中的未定义行为;任何事情都可能发生,包括从你鼻子里飞出的恶魔。

于 2011-04-21T21:27:28.803 回答
8

正如约翰的回答中提到的,您可以禁用验证(将类放在 bootclasspath 上也应该有效),加载并执行一个类,Throwable成功抛出一个非类。

令人惊讶的是,它并不一定会导致崩溃!

只要您不Throwable隐式或显式调用方法,一切都会正常工作:

.source ThrowObject.j
.class public ThrowObject
.super java/lang/Object

.method public <init>()V
    aload_0
    invokenonvirtual java/lang/Object/<init>()V
    return
.end method

.method public static main([Ljava/lang/String;)V

    new java/lang/Object
    dup
    invokenonvirtual java/lang/Object/<init>()V

  BeforeThrow:
    athrow

  AfterThrow:

    return

  CatchThrow:

    getstatic java/lang/System/out Ljava/io/PrintStream;
    ldc "Thrown and catched Object successfully!"
    invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V
    return

  .catch all from BeforeThrow to AfterThrow using CatchThrow
.end method

结果:

% java -Xverify:none ThrowObject
Thrown and catched Object successfully!
于 2012-09-10T13:24:35.813 回答
5

[...]禁用字节码验证器[...]

字节码验证是 JVM 规范的一部分,所以如果你禁用它(或以其他方式篡改 JVM),你可以根据实现,做任何我认为的事情(包括抛出原语等)。

引用 JVM 规范:

objectref必须是引用类型,并且必须引用作为 Throwable 类或 Throwable 子类的实例的对象

即,您的问题可以解释为“如果 JVM 偏离规范,它是否可以做一些奇怪的事情,例如抛出原始对象”,答案当然是肯定的。

于 2011-04-21T21:25:27.040 回答