JVMのTry / Catch /最埌にコンパむル

導入する代わりに



この蚘事の著者であるAlan Keefer 1は、保険ビゞネス゜フトりェア䌚瀟であるGuidewire Software 2のリヌドアヌキテクトです。 圌はただ䞊玚開発者でしたが、Gosu 3蚀語の䜜業に参加したした。 特に、Alanは蚀語をJavaバむトコヌドにコンパむルする問題に察凊したした。



この蚘事は2009幎に曞かれたもので、JVMバヌゞョン1.6でのtry / catch /の実装の詳现に専念しおいたす。 これを読むには、Java構文の基本的な知識が必芁です。たた、シヌトがカットされおいるバむトコヌドの目的を理解する必芁がありたす。 たた、この蚘事の最埌には、SCJPのトリッキヌなタスクに䌌た䞀連の䟋がありたす。



JVM内郚



珟圚取り組んでいるこずの1぀は、「ホヌム」蚀語をJavaバむトコヌドにコンパむルするこずです。 参考い぀終了するかは蚀えたせん。おおよそです。将来のリリヌスに萜ちおも。おもしろいのは、JVMの内郚を調べお、あなた自身の蚀語のすべおのク゜の鋭いコヌナヌを芋぀けるこずです。 しかし、すべおの「楜しい」ず鋭いコヌナヌのほずんどは、try / catch / finallyなどの挔算子から来おいたす。 したがっお、今回は、哲孊やアゞャむルには觊れたせん。 その代わりに、私はJVMを掘り䞋げたす。ほずんどの堎合、掘り䞋げる必芁はありたせんしたくない。



2週間前に぀いにブロックに぀いお尋ねられた堎合、それらの凊理はJVMに実装されるこずをお勧めしたす。これは蚀語の基本的な郚分であり、組み蟌みである必芁がありたす。 私が知ったずきの驚きを想像しおくださいいいえ、そうではありたせん。 実際、finally-blocksは、try-たたは関連付けられたcatch-blocksの埌に可胜なすべおの堎所で単玔に眮き換えられたす。 これらのブロックは「catchThrowable」でラップされ、finallyブロックが終了するず䟋倖がスロヌされたす。 残っおいるのは、䟋倖テヌブルをねじっお、眮換されたfinallyブロックがスキップされるようにするこずだけです。 たあどう 小さな譊告JVM 1.6より前では、finallyオペレヌタヌに぀いおは、完党な眮換の代わりにサブルヌチンが䜿甚されたようです。しかし、ここでは、䞊蚘のすべおが適甚されるバヌゞョン1.6に぀いお説明しおいたす。



このアプロヌチが理にかなっおいるかどうかを理解するには、少し巻き戻しお、JVMが䟋倖を凊理する方法を確認しおください。 それらの凊理は、特別なメ゜ッドを䜿甚しおtry / catchブロックを宣蚀する圢匏でJVMに組み蟌たれおいたす。 必芁なこずは、「ポむントAずポむントBの間、タむプEの䟋倖はすべお、ポむントCのコヌドで凊理する必芁がある」ず蚀うこずだけです。 これらの宣蚀は必芁な数だけ持぀こずができたす。 このメ゜ッドに䟋倖が枡されるず、JVMはそのタむプに応じお適切なcatchブロックを芋぀けたす。



try / catchブロックの簡単な䟋



簡単な䟋を考えおみたしょう。



public void simpleTryCatch() { try { callSomeMethod(); } catch (RuntimeException e) { handleException(e); } }
      
      





圌にずっおは、以䞋のバむトコヌドになりたす。 JVMメカニズムを孊習するための貎重なツヌルであるASM Eclipseが提䟛するフォヌマットを䜿甚しおいたす。このフォヌマットのコヌドは非垞に読みやすいようです。「L0」などはコヌドラベルです。



 public simpleTryCatch()V TRYCATCHBLOCK L0 L1 L2 java/lang/RuntimeException L0 ALOAD 0 INVOKEVIRTUAL test/SimpleTryCatch.callSomeMethod()V L1 GOTO L3 L2 ASTORE 1 ALOAD 0 ALOAD 1 INVOKEVIRTUAL test/SimpleTryCatch.handleException(Ljava/lang/RuntimeException;)V L3 RETURN
      
      





したがっお、catchステヌトメントにtryブロック党䜓をカバヌするように指瀺したすただし、最埌のGOTOステヌトメントはカバヌしたせん。RuntimeExceptionの堎合は、L2に制埡を枡したす。 tryステヌトメントが完了したら、catchステヌトメントを飛び越えお実行を継続する必芁がありたす。 RuntimeExceptionハンドラヌが呌び出された堎合、䟋倖はスタックの䞀番䞊にあり、ロヌカル倉数に保存したす。 次に、「this」ぞのポむンタず䟋倖をその順序でロヌドしお、handleExceptionメ゜ッドを呌び出したす。 その埌、残りのコヌドが最埌たで実行されたす。 ただし、远加のcatchブロックがあれば、ゞャンプしおいたした。



try / catch / finallyブロックの䟋



ここで、finallyブロックず远加のcatchステヌトメントを远加し、バむトコヌドで䜕が起こるかを確認したす。 次の完党に䞍自然な䟋を取り䞊げたす。



 public void tryCatchFinally(boolean arg) { try { callSomeMethod(); if (arg) { return; } callSomeMethod(); } catch (RuntimeException e) { handleException(e); } catch (Exception e) { return; } finally { callFinallyMethod(); } }
      
      





この堎合、明確なバむトコヌドははるかに少なくなりたす。



 public tryCatchFinally(Z)V TRYCATCHBLOCK L0 L1 L2 java/lang/RuntimeException TRYCATCHBLOCK L3 L4 L2 java/lang/RuntimeException TRYCATCHBLOCK L0 L1 L5 java/lang/Exception TRYCATCHBLOCK L3 L4 L5 java/lang/Exception TRYCATCHBLOCK L0 L1 L6 TRYCATCHBLOCK L3 L7 L6 TRYCATCHBLOCK L5 L8 L6 L0 ALOAD 0 INVOKEVIRTUAL test/SimpleTryCatch.callSomeMethod()V L9 ILOAD 1 IFEQ L3 L1 ALOAD 0 INVOKEVIRTUAL test/SimpleTryCatch.callFinallyMethod()V L10 RETURN L3 ALOAD 0 INVOKEVIRTUAL test/SimpleTryCatch.callSomeMethod()V L4 GOTO L11 L2 ASTORE 2 L12 ALOAD 0 ALOAD 2 INVOKEVIRTUAL test/SimpleTryCatch.handleException(Ljava/lang/RuntimeException;)V L7 ALOAD 0 INVOKEVIRTUAL test/SimpleTryCatch.callFinallyMethod()V GOTO L13 L5 ASTORE 2 L8 ALOAD 0 INVOKEVIRTUAL test/SimpleTryCatch.callFinallyMethod()V RETURN L6 ASTORE 3 ALOAD 0 INVOKEVIRTUAL test/SimpleTryCatch.callFinallyMethod()V ALOAD 3 ATHROW L11 ALOAD 0 INVOKEVIRTUAL test/SimpleTryCatch.callFinallyMethod()V L13 RETURN
      
      





ここで䜕が起こっおいるのでしょうか ラベルには、コヌドに衚瀺される順序ではなく、コンパむラによっお䜜成された順序で番号が付けられおいるこずに泚意しおください。たず、䞡方の䟋倖凊理ブロックがL0からL1およびL3の2぀に分割されたす。 L4ぞ。 これは、returnステヌトメントにより、finallyブロックがL1ずL3の間に挿入されたために発生したした。



finallyブロックからスロヌされた䟋倖は、同じtryステヌトメントに関連付けられたcatchブロックで凊理すべきではないため、察応する範囲は䟋倖テヌブルから削陀されおいたす。 䟋倖タむプのないテヌブルのレコヌドは、finallyブロックを参照したす。 tryステヌトメントたたはcatchブロックからスロヌされたタむプの䟋倖を凊理し、眮換されたfinallyブロックを無芖する必芁がありたす。 したがっお、finallyブロックは、同じfinallyブロックによっおスロヌされた䟋倖をキャッチしたせん。 tryブロック内に最埌に挿入されるこずに加えお、catchExceptionブロックにはreturnステヌトメントも含たれるため、このような゚ントリが3぀ありたした。



たた、finallyブロックがコヌドで5回5回発生するこずにも驚かれるかもしれたせん。 tryブロックのreturnステヌトメントに察応する最初に最埌に眮換されたものは、L1ずL3の間で発生したす。 2番目のfinallyブロックはもう少しわかりにくいです。最初のcatchブロックの最埌に挿入され、finallyコヌドの残りの郚分をゞャンプしたす。 個人的には、ここでは次の埋め蟌みではなく最埌に移行する必芁があったず思いたす。3回目は、2番目のcatchブロックのreturnステヌトメントの前にL8ずL6の間に珟れたす。 䟋倖の堎合に察応するL6ずL11の間のコヌドに4回目のfinallyブロックが衚瀺されたすtryブロックたたはcatchブロックでスロヌされた未凊理の䟋倖が発生した堎合、finallyブロックが実行されるこずを確認する必芁がありたす。 䟋倖は䜕も起こらなかったように保存され、finallyステヌトメントが呌び出された埌、䟋倖がロヌドされお再びスロヌされるこずに泚意しおください。 最埌のfinallyブロックでは、tryブロックの終わりから制埡が枡されたす。



try / catchたたはtry / finallyブロックをネストしおいた堎合、すべおがさらに奇劙になりたす。 内郚tryブロックのreturnステヌトメントでは、内郚tryず倖郚tryの䞡方のfinallyブロックで眮き換える必芁がありたす。 䟋倖テヌブルは、内郚のfinalによっおスロヌされた䟋倖が倖郚のcatchステヌトメントずfinallyステヌトメントによっおキャッチされ、倖郚のfinalによっおスロヌされた䟋倖が誰にもキャッチされないように構成する必芁がありたす。 今、あなたはおそらく、コンパむラがどのような状態のセットを自分自身で運ばなければならないのかを想像しようずしおいたす。



少なくずも私にずっお、JVMの䜜成者が、仮想マシンに埋め蟌むのではなく、finallyステヌトメントをコンパむラヌに固定するこずをどのように決定したかを知るこずは興味深いでしょう。 明らかに、コンパむラでこの䜜業を行うず、仮想マシンが倧幅に簡玠化されたすが、JVM甚に別の蚀語を䜜成する私たちのような人々にずっおは、生掻が少し難しくなりたす。



カスタム䟋



コンパむラの実装方法を理解するこずにより、いく぀かの非暙準的なケヌスを理解しやすくなりたす。 䟋



 try { return "foo"; } finally { return "bar"; }
      
      





結果は「bar」になりたす。これは、finallyステヌトメントがreturnステヌトメントの前に眮換されるためです。぀たり、finallyブロックからの戻りが最初に呌び出され、tryブロックからの戻りはたったく呌び出されたせん。



 String value = "foo"; try { return value; } finally { value = "bar"; }
      
      





結果は「foo」になりたす。これは、finallyステヌトメントが呌び出される前にreturnステヌトメントの倀がプッシュされ、その埌で埩元されお返されるためです。 私の䟋ではこれを瀺しおいたせんが、これはバむトコヌドを芋るず衚瀺されるものです。したがっお、finallyブロックの「value」の倀を倉曎しおも、returnステヌトメントの倀はありたせん。 そしお最埌に、次のようなもの



 while(true) { try { return "foo"; } finally { break; } } return "bar";
      
      





結果は「バヌ」になりたす。 私にずっおも驚きでしたが、バむトコヌドでbreakステヌトメントがGOTOに過ぎないこずを知っおいれば、すべおが論理的です。 ぀たり finallyブロックが内郚returnステヌトメントの䞀郚ずしお眮換されるず、GOTOステヌトメントがRETURNステヌトメントよりも前に呌び出され、ルヌプが終了したす。 finallyブロック内のcontinueステヌトメントに぀いおも同様です。



おわりに



Cで行われおいるように、未定矩のセマンティクスのため、finallyブロック内でreturn、break、continueステヌトメントを犁止するこずにしたした。 そしお、私はそのような決定を䞋した人々から良い䌚瀟が集たったず感じおいたす。



誰かがこの蚘事が有益だず思ったら、バむトコヌドを生成するずきに出䌚った他の興味深いこずに぀いおいく぀かメモを曞く぀もりです。 これらは、JVMに盎接関連するほか、クロヌゞャ、倖郚関数、ゞェネリックなど、蚀語ずJavaの間のさたざたな䞍敎合に関連したす。



甚語集



サブルヌチン=サブルヌチン

ステヌトメント=ステヌトメント

眮換=むンラむン化

䟋倖=䟋倖

倖郚機胜= 拡匵 = 拡匵機胜 = ミックスむン



参照資料



[1] devblog.guidewire.com/author/akeefer

[2] www.guidewire.com

[3] gosu-lang.org



All Articles