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