ラベル ASM の投稿を表示しています。 すべての投稿を表示
ラベル ASM の投稿を表示しています。 すべての投稿を表示

2015年11月16日月曜日

ASMのドキュメントを読む(Core API - Methods - Interfaces and components前半)

前回の続き。今回からASMについて。

ASMではVisitorパターンを使って、バイトコードを生成したり変更したりする。コンパイル後のクラスファイル群は、クラスがあったりメソッドがあったりアノテーションが付いていたりと複雑なツリー状の構造をしているので、ツリーの中のどの要素をどう操作するかを記述していくことになる。

Visitorパターンでは複数の要素を巡って処理するものだが、ASMではVisitorクラスのコンストラクタに別のVisitorオブジェクトを登録するところがポイント。インスタンスを作る時にVisitor1→Visitor2→Visitor3のようにVisitor同士を一方向に繋げるようになっている。ClassReaderやClassWriterのような入出力もVisitorでできているので、既存のクラスファイルを読み込んで、複数箇所を変換して、出力するという一連のバイトコード操作の処理フローを作ることができる。

MethodVisitorの場合、プログラマーの責任において次の順番で呼びだすことが定められている。
visitAnnotationDefault?
( visitAnnotation | visitParameterAnnotation | visitAttribute )* ( visitCode
( visitTryCatchBlock | visitLabel | visitFrame | visitXxxInsn | visitLocalVariable | visitLineNumber )*
visitMaxs )? visitEnd 

ややこしいことに、ClassWriterで出力するためには、フレームとローカル変数、オペランドスタックのサイズを計算する必要がある。ASMに自動計算させることもできるが10%〜2倍程度遅くなる。new ClassWriter(ClassWriter.COMPUTE_FRAMES)を指定してもvisitMaxs()メソッドの呼び出しは省略できないことに注意。引数は無視されるのでvisitMaxs(0,0)でOK

ASMのドキュメントを読む(Core API - Methods - Structure)

Javaバイトコード操作のためのライブラリであるASMのガイドのメモの第1弾。
最新版はバージョン5系だが、ガイドは4のものしかない。
http://download.forge.objectweb.org/asm/asm4-guide.pdf

今回はASMまでは言及せずに、JVMの実行モデルについての概念の整理。
  • 各スレッドは実行スタックを持っている(ヒープはスレッドが共有する)
  • 実行スタックには複数のフレームから成る(フレーム1つが、1回のメソッド呼び出しに相当する)
  • フレームはローカル変数部とオペランドスタックから成る(ローカル変数にはインデックスが付いたリスト、オペランドスタックはバイトコード命令のためのスタックである。
  • ちょっと複雑だが、実行スタックの中にオペランドスタックが入っていることになる。
  • バイトコード命令は、opcodeとopcodeごとに決まった数の引数から成る(opcodeは命令の内容を識別するコードで1バイトで表現される。だから「バイトコード」と呼ぶ。)
  • オペランドスタックにはバイトコード命令そのものと、そのバイトコード命令で使うデータも格納する(つまりデータの格納場所としてはローカル変数部とオペランドスタックの2箇所がある)
  • バイトコード命令には、ローカル変数部とオペランドスタックの間でデータを移動させる命令と、オペランドスタックの中だけで操作する命令の2種類がある(あらゆるメソッドが1フレームという単純なルールで表現できるのはちょっと不思議な感じがする)
Javaはオブジェクト指向言語なので、メソッド呼び出し=メッセージ・パッシングである。あるインスタンスメソッドを呼び出す場合、ローカル変数部にはthisと引数が格納される。実際は、ローカル変数はthisのインスタンスはヒープに存在してローカル変数部にはポインタが入っているだけ。longやdouble型の値は、2スロット分を使って格納する(つまり1スロットは4バイト?)。

ASMのガイドにはいろんな例が載っている。例えばpkg.Beanクラスのfというint型フィールドのgetterは以下のようになる。
ALOAD 0・・・ローカル変数部に格納されているthisをオペランドスタックに積む。
GETFIELD pkg/Bean f I・・・pkg.Beanクラスのオブジェクトのfというフィールドのint型の値を取得してオペランドスタックに積む。thisはpopされる。
IRETURN・・・オペランドスタック上のint型の値をpopしてメソッドの戻り値として返す。

fという名前は定数プールから名前解決される。ここを見るとバイトコード命令の実行前後でオペランドスタックの状態がどのように遷移するかがわかりやすい。

次回からASMでバイトコードを扱う方法に入っていく。

DockerでMQ環境を作る

DockerHubの ibmcom/mq を使う。ファイルシステムとしてtmpfsは使えないので これ を参考に/mnt/sda1に置き換える。 brew install docker docker-machine docker-machine create --drive...