web-dev-qa-db-ja.com

Javaで何を投げることができますか?

従来の通念では、JavaではThrowableを拡張するオブジェクトしかスローできないと言われていますが、バイトコードベリファイアを無効にしてJavaを取得し、任意のオブジェクトをスローするコードをコンパイルして実行することは可能です-またはプリミティブさえ?

JVMの athrow を調べたところ、オペランドスタックの最初のobjrefがポップされます。しかし、その参照が実行時にThrowableを指しているかどうかをチェックしますか?

62

これは、JVMの実装によって異なります。 Java VM仕様によると、オブジェクトがThrowableでない場合、未定義の動作です。

Objectrefmustはタイプ参照であり、クラス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が例外の詳細を出力しようとするとクラッシュしたように見えます。これら2つのプログラムを比較してください。最初のプログラムはExceptionをスローし、2番目のプログラムは上記の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プログラムでの動作。鼻から飛び出す悪魔など、何でも起こり得ます。

72
John Kugelman

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!
7
soc

[...] バイトコードベリファイアを無効にする [...]

バイトコード検証はJVM仕様の一部であるため、これを無効にすると(または、他の方法でJVMを改ざんすると)、実装によっては、私が想定するほぼすべてのこと(プリミティブのスローなど)を実行できます。

JVM仕様からの引用:

Objectref タイプreferenceである必要がありますおよびクラスThrowableのインスタンスであるオブジェクトを参照する必要がありますまたはThrowableのサブクラスです。

つまり、あなたの質問は "JVMが仕様から逸脱した場合、プリミティブをスローするなどの奇妙なことを行うことができますか"と解釈できます。もちろん、はい。

5
aioobe