你能用Java做什么?

Nul*_*ion 61 java exception throw

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

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

Joh*_*ica 73

这取决于您的JVM实现.根据Java VM规范,如果对象不是,则它是未定义的行为Throwable.

objectref 必须是类型引用,并且必须引用一个对象,该对象是Throwable类的实例或Throwable的子类.

第6.1节"'必须'的含义"中:

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

我写了使用的测试程序茉莉汇编这不等同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
Run Code Online (Sandbox Code Playgroud)

禁用字节码验证程序允许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" #
Run Code Online (Sandbox Code Playgroud)

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


soc*_*soc 8

正如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
Run Code Online (Sandbox Code Playgroud)

结果:

% java -Xverify:none ThrowObject
Thrown and catched Object successfully!
Run Code Online (Sandbox Code Playgroud)


aio*_*obe 5

[...] 禁用字节码验证程序 [...]

字节码验证是JVM规范的一部分,因此如果你禁用它(或以其他方式篡改JVM),你可以根据实现做任何事情(包括抛出基元等),我会假设.

引用JVM规范:

objectref 必须是类型引用,并且必须引用一个对象,该对象是Throwable类的实例Throwable的子类.

也就是说,你的问题可以被解释为"如果一个JVM偏离规范,它可以做一些奇怪的东西,比如抛出原始数据",答案当然是肯定的.