传统观点认为你只能抛出在Java中扩展Throwable
的对象,但是是否可以禁用字节码验证器并让Java编译并运行抛出任意对象的代码 - 甚至是原语?
我查看了JVM的athrow
,它将在操作数堆栈上弹出第一个objref;但它是否会检查所述参考是否在运行时指向Throwable
?
答案 0 :(得分:72)
这取决于您的JVM实现。根据Java VM规范,如果对象不是Throwable
,则它是未定义的行为。
objectref 必须属于类型引用,并且必须引用一个对象,该对象是Throwable类的实例或Throwable的子类。
在section 6.1, "The Meaning of 'Must'"中:
如果在运行时不满足指令描述中的某些约束(“必须”或“必须不”),则Java虚拟机的行为是未定义的。
我使用Jasmin assembler编写了一个测试程序,它与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程序中未定义的行为;任何事情都可能发生,包括恶魔飞出你的鼻子。
答案 1 :(得分:7)
如John的回答所述,您可以禁用验证(将类放在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!
答案 2 :(得分:5)
[...] 禁用字节码验证程序 [...]
字节码验证是JVM规范的一部分,因此如果你禁用它(或以其他方式篡改JVM),你可以根据实现做任何事情(包括抛出原语等),我会假设。
来自JVM规范的引用:
objectref 必须是类型引用,必须引用一个对象,该对象是Throwable类的实例或Throwable的子类。
即,您的问题可以解释为“如果JVM偏离规范,它可以做奇怪的事情,例如抛出原始数据”,答案当然是,是的。