Diksamに例外処理をつけるため、Javaの仕様を参考にしようとしているんですが。
try catchのfinally節は、「何があっても通る」ところなので、たとえば以下のようなソースでは、
for (;;) { try { … break; } catch (Exception e) { … return; } finally { … } }
try節の中のbreakやcatch節の中のreturnでfor文やメソッドを抜ける際でもfinallyの中は通らなければなりません。しかも、finally実行後に行うことはbreakだったりreturnだったりと異なるので、これは単にジャンプ命令で引き回すだけでは実現が難しそうです。
そのためにjsr(jump subroutine)という、関数内のサブルーチンコールのインストラクションがJVMにはあるのだと私は理解してました。
JVMの仕様書を見ると、
http://java.sun.com/docs/books/jvms/second_edition/html/Compiling.doc.html#13789
void tryFinally() { try { tryItOut(); } finally { wrapItUp(); } }
というソースから、以下のようなバイトコードが生成されるそうです。
Method void tryFinally() 0 aload_0 // Beginning of try block 1 invokevirtual #6 // Method Example.tryItOut()V 4 jsr 14 // Call finally block 7 return // End of try block 8 astore_1 // Beginning of handler for any throw 9 jsr 14 // Call finally block 12 aload_1 // Push thrown value 13 athrow // ...and rethrow the value to the invoker 14 astore_2 // Beginning of finally block 15 aload_0 // Push this 16 invokevirtual #5 // Method Example.wrapItUp()V 19 ret 2 // Return from finally block Exception table: From To Target Type 0 4 8 any
添字4、9の箇所でfinallyの開始店である14バイト目にサブルーチンジャンプしています。
じゃあbreakとかreturnとか使ったら、その直前にjsrを出力するのかな、と思い、試してみました。
class ExceptionTest2 { public static void main(String[] args) { for (;;) { try { System.out.println("hello, world."); break; } catch (Exception e) { System.out.println("catch"); } finally { System.out.println("finally"); } } } }
これをコンパイルして、javap -cで見てみると… あれ?
public static void main(java.lang.String[]); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3; //String hello, world. 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 11: ldc #5; //String finally 13: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 16: goto 53 19: astore_1 20: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 23: ldc #7; //String catch 25: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 28: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 31: ldc #5; //String finally 33: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 36: goto 50 39: astore_2 40: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 43: ldc #5; //String finally 45: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 48: aload_2 49: athrow 50: goto 0 53: return Exception table: from to target type 0 8 19 Class java/lang/Exception 0 8 39 any 19 28 39 any 39 40 39 any
ソースには3箇所しかないSystem.out.println()の呼び出しが5箇所に増えています。
finally節の「System.out.println("finally");」を、try節のbreakの直前とcatch節の末尾に複製しているように見えます。
finally節が短かったからコンパイラが展開しちゃったのかなあ、と思い、ちょっと長くしてみましたが、
class ExceptionTest3 { public static void main(String[] args) { for (;;) { try { System.out.println("hello, world."); break; } catch (Exception e) { System.out.println("catch"); } finally { System.out.println("finally1"); System.out.println("finally2"); System.out.println("finally3"); System.out.println("finally4"); System.out.println("finally5"); System.out.println("finally6"); System.out.println("finally7"); System.out.println("finally8"); System.out.println("finally9"); System.out.println("finally10"); System.out.println("finally11"); System.out.println("finally12"); System.out.println("finally13"); System.out.println("finally14"); } } } }
public static void main(java.lang.String[]); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3; //String hello, world. 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 11: ldc #5; //String finally1 13: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 16: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 19: ldc #6; //String finally2 21: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 24: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 27: ldc #7; //String finally3 29: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 32: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 35: ldc #8; //String finally4 37: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 40: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 43: ldc #9; //String finally5 45: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 48: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 51: ldc #10; //String finally6 53: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 56: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 59: ldc #11; //String finally7 61: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 64: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 67: ldc #12; //String finally8 69: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 72: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 75: ldc #13; //String finally9 77: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 80: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 83: ldc #14; //String finally10 85: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 88: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 91: ldc #15; //String finally11 93: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 96: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 99: ldc #16; //String finally12 101: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 104: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 107: ldc #17; //String finally13 109: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 112: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 115: ldc #18; //String finally14 117: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 120: goto 365 123: astore_1 124: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 127: ldc #20; //String catch 129: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 132: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 135: ldc #5; //String finally1 137: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 140: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 143: ldc #6; //String finally2 145: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 148: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 151: ldc #7; //String finally3 153: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 156: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 159: ldc #8; //String finally4 161: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 164: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 167: ldc #9; //String finally5 169: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 172: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 175: ldc #10; //String finally6 177: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 180: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 183: ldc #11; //String finally7 185: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 188: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 191: ldc #12; //String finally8 193: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 196: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 199: ldc #13; //String finally9 201: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 204: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 207: ldc #14; //String finally10 209: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 212: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 215: ldc #15; //String finally11 217: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 220: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 223: ldc #16; //String finally12 225: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 228: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 231: ldc #17; //String finally13 233: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 236: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 239: ldc #18; //String finally14 241: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 244: goto 362 247: astore_2 248: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 251: ldc #5; //String finally1 253: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 256: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 259: ldc #6; //String finally2 261: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 264: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 267: ldc #7; //String finally3 269: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 272: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 275: ldc #8; //String finally4 277: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 280: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 283: ldc #9; //String finally5 285: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 288: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 291: ldc #10; //String finally6 293: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 296: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 299: ldc #11; //String finally7 301: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 304: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 307: ldc #12; //String finally8 309: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 312: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 315: ldc #13; //String finally9 317: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 320: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 323: ldc #14; //String finally10 325: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 328: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 331: ldc #15; //String finally11 333: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 336: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 339: ldc #16; //String finally12 341: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 344: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 347: ldc #17; //String finally13 349: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 352: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 355: ldc #18; //String finally14 357: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 360: aload_2 361: athrow 362: goto 0 365: return Exception table: from to target type 0 8 123 Class java/lang/Exception 0 8 247 any 123 132 247 any 247 248 247 any
…いいのかなあ、これ。
ていうかそもそも仕様書にあるサンプルならjsrが出力されるのかよ、と、足りないコードを補って以下で試してみたんですが、
class TryFinally { void tryItOut() {} void wrapItUp() {} void tryFinally() { try { tryItOut(); } finally { wrapItUp(); } } }
バイトコードはこうなりました。
void tryFinally(); Code: 0: aload_0 1: invokevirtual #2; //Method tryItOut:()V 4: aload_0 5: invokevirtual #3; //Method wrapItUp:()V 8: goto 18 11: astore_1 12: aload_0 13: invokevirtual #3; //Method wrapItUp:()V 16: aload_1 17: athrow 18: return Exception table: from to target type 0 4 11 any 11 12 11 any
…えーっと。
※ javac 1.6.0_02にて。