传统智慧认为在Java中只能抛出扩展了Throwable
的对象,但是是否可能禁用字节码验证器并使Java编译和运行代码抛出任意对象 - 甚至原始类型?
我查看了JVM的athrow
,它将弹出操作数栈上的第一个objref;但是它会在运行时检查所述引用是否指向Throwable
吗?
传统智慧认为在Java中只能抛出扩展了Throwable
的对象,但是是否可能禁用字节码验证器并使Java编译和运行代码抛出任意对象 - 甚至原始类型?
我查看了JVM的athrow
,它将弹出操作数栈上的第一个objref;但是它会在运行时检查所述引用是否指向Throwable
吗?
这取决于您的JVM实现。根据Java VM规范,如果对象不是Throwable
,则未定义行为。
必须使用引用类型的 objectref 参考
Throwable
类或其子类的对象。
如果某个指令描述中的某个约束(“Must”或“must not”)在运行时未满足,则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
# 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" #
正如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!
[...] 禁用字节码验证器 [...]
字节码验证是JVM规范的一部分,因此如果您禁用它(或以其他方式篡改JVM),则可以根据实现情况执行几乎任何操作(包括抛出原始类型等)。 我会假设。
JVM规范的引用:
objectref 必须是引用类型,并且必须引用Throwable类的实例或Throwable子类的对象。
也就是说,您的问题可以解释为“如果JVM偏离规范,是否可以执行奇怪的操作,例如抛出基元类型”,答案当然是可以的。