WEBワークショップ

HOME > WEBワークショップ > Javassistチュートリアル



Javassistチュートリアル


5.バイトコードレベルAPI

Javassist は、クラスファイルを直接編集するための低レベルAPIも提供しています。このレベルのAPIではクラスファイルのどのような編集も可能となるため、利用するにはJava バイトコードとクラスファイルフォーマットに対する詳細な知識が必要となります。

●5.1 ClassFile オブジェクトの取得

javassist.bytecode.ClassFile オブジェクトは、クラスファイルを表します。このオブジェクトを取得するには、CtClass の getClassFile() メソッドを呼び出します。

また、クラスファイルから直接 javassist.bytecode.ClassFile オブジェクトを生成することもできます。

例えば、このコードでは Point.class から ClassFile オブジェクトを生成しています。

 BufferedInputStream fin
     = new BufferedInputStream(new FileInputStream("Point.class"));
 ClassFile cf = new ClassFile(new DataInputStream(fin));


ClassFile オブジェクトは、クラスファイルに書き戻すことができます。ClassFile の write() メソッドはクラスファイルの内容を与えられた DataOutputStream へ出力します。

●5.2 メンバの追加と削除

ClassFile は、フィールドやメソッドを追加のために、 addField()、addMethod()を提供します(バイトコードレベルでは、コンストラクタもメソッドの1つとみなされます)。また、クラスファイルに属性を追加するための addAttribute() メソッドも提供されています。

FieldInfo、MethodInfo、AttributeInfo オブジェクトは、ConstPool(コンスタント・プール・テーブル)オブジェクトへのリンクを含みます。ConstPoolオブジェクトは、ClassFile オブジェクトと FieldInfo オブジェクト (または MethodInfo オブジェクトなど)に共通なものでなければなりません。言い換えれば、FieldInfo (または MethodInfo など) オブジェクトは異なる ClassFile オブジェクトの間で共有されてはいけないのです。

フィールドやメソッドを削除するには、まず最初にそのクラスにおける全てのフィールドを含む java.util.List メソッドを取得します。getFields() や getMethods() メソッドがそのリストを返します。属性も同様の方法で削除することができます。FieldInfo や MethodInfo の getAttributes() メソッドを呼び出すことで属性のリストを取得できるので、そこから削除してください。

●5.3 メソッド本体のトラバース

ClassFile オブジェクトは、クラスファイルに書き戻すことができます。ClassFile の write() メソッドはクラスファイルの内容を与えられた DataOutputStream へ出力します。

CodeIterator を利用すれば、メソッド本体の中の全てのバイトコード命令を調べることができます。このオブジェクトを取得するには、以下のようにしてください。

 ClassFile cf = ... ;
 MethodInfo minfo = cf.getMethod("move");    // we assume move is not overloaded.
 CodeAttribute ca = minfo.getCodeAttribute();
 CodeIterator i = ca.iterator();


CodeIterator オブジェクトにより、最初から最後まで全てのバイトコードを順番に調査することができます。以下のメソッドは、CodeIterator で宣言されているメソッドの一部です。

void begin()
最初の命令に移動します。

void move(int index)
引数で与えられたインデックスの命令に移動します。

hasNext()
次の命令が存在する場合、true を返します。

next()
次の命令のインデックスを返します。次の命令のオペコードを返すわけではないことに注意してください。

int byteAt(int index)
指定されたインデックスを符号なし8ビットの値で返します。

int u16bitAt(int index)
指定されたインデックスを符号なし16ビットの値で返します。

int write(byte[] code, int index)
指定されたインデックスにバイト配列を書き込みます。

void insert(int index, byte[] code)
指定されたインデックスにバイト配列を挿入します。このとき、分岐オフセット等は自動的に変換されます。

●5.4 バイトコードシーケンスの生成

Bytecode オブジェクトはバイトコード命令のシーケンスを表します。これは、バイトコードの可変配列です。

 ConstPool cp = ...;    // constant pool table
 Bytecode b = new Bytecode(cp, 1, 0);
 b.addIconst(3);
 b.addReturn(CtClass.intType);
 CodeAttribute ca = b.toCodeAttribute();


このプログラムでは、以下のようなバイトコードシーケンスを生成します。

 iconst_3
 ireturn


Bytecode の get() メソッドを呼び出すことで、このシーケンスを含むバイト配列を得ることができます。

Bytecode クラスはシーケンスに特定の命令を追加するために大量のメソッドを提供しますが、8bit のオペコードを追加するための addOpcode() メソッドと、インデックスを追加するための addIndex() メソッドも提供しています。各オペコードの8ビット値は Opcode インターフェースで提供されています。

addOpcode() メソッドをはじめとする特定の命令を追加するためのメソッド群は、制御フローが分岐を含まない限り、スタックサイズの最大値を自動的に変更します。この値は Bytecode オブジェクトの getMaxStack() メソッドによって取得することができます。これは、Bytecode オブジェクトから生成される CodeAttribute オブジェクトにも反映されます。メソッドの最大スタックサイズを再計算するには、CodeAttribute クラスの computeMaxStack() メソッドを呼び出してください。


1 2 3 4 5

ページトップへ
Java? は、米国Sun Microsystems,Inc. の登録商標です。
原著:Copyright c 2000-2005 O’Reilly Media, Inc. All rights reserved.
邦訳:Copyright c 2005 Acroquest, Co.,Ltd. All rights reserved.