従来の通念では、JavaではThrowable
を拡張するオブジェクトしかスローできないと言われていますが、バイトコードベリファイアを無効にしてJavaを取得し、任意のオブジェクトをスローするコードをコンパイルして実行することは可能です-またはプリミティブさえ?
JVMの athrow
を調べたところ、オペランドスタックの最初のobjrefがポップされます。しかし、その参照が実行時にThrowable
を指しているかどうかをチェックしますか?
これは、JVMの実装によって異なります。 Java VM仕様によると、オブジェクトがThrowable
でない場合、未定義の動作です。
Objectrefmustはタイプ参照であり、クラスThrowableまたはThrowableのサブクラスのインスタンスであるオブジェクトを参照する必要があります。
実行時に命令記述の制約(「必須」または「必須」)が満たされない場合、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プログラムでの動作。鼻から飛び出す悪魔など、何でも起こり得ます。
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 タイプreferenceである必要がありますおよびクラスThrowableのインスタンスであるオブジェクトを参照する必要がありますまたはThrowableのサブクラスです。
つまり、あなたの質問は "JVMが仕様から逸脱した場合、プリミティブをスローするなどの奇妙なことを行うことができますか"と解釈できます。もちろん、はい。