Javaの例外処理

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にて。