Josh : バイトコードレベルでのJava用 Aspect Weaver 中川 清志(東工大) 立堀 道昭(筑波大) 千葉 滋 (東工大)
オブジェクト指向の限界 オブジェクト指向はモジュラリティが優れているが限界もある ある種の処理は複数のオブジェクトに散らばってしまう ログ出力,メモリ管理などの システマティックな処理 その結果保守性が悪化し修正も困難になる
散らばってしまう例 : ログ出力 class Bike{ void start() { Log.print(“start”); .. } class Car{ void start() { Log.print(“start”); .. } void back() { Log.print(“back”); class Log { static void print(String msg) { System.out.println(msg); } ログ出力をするタイミングを変えるような変更の度に, Car, Bikeクラスのソースコードを修正しなければならない
アスペクト指向 散らばっていたコードをアスペクトとしてモジュール化するのがアスペクト指向 アスペクトはオブジェクトによるモジュール分割を補完するもの この2種類のモジュールを合成して,1つのプログラムを作り出すことを weave という weave 実行
AspectJ の新処理系 Josh AspectJ 言語 AspectJ 言語の処理系(weaver) 汎用アスペクト指向言語の代表的なもの AspectJ 言語の処理系(weaver) ajc XEROX 版標準処理系 Josh 我々が新たに開発した処理系
AspectJの言語仕様(1) Introduction 既存クラスに新しいメンバを加える 既存クラスの階層構造をかえる フィールド,メソッドの追加 既存クラスの階層構造をかえる スーパクラス,インタフェースの変更 private int Point.z; public int Point.getZ() { return z; } declare parents : Point implements java.io.Serializable
AspectJの言語仕様(2) Advice Join-Point Join-Point にコード断片を埋め込むように指示 before, after, aroundの3種類 Join-Point プログラム実行の節目 例 : メソッド呼び出し,フィールド書き込み etc. before() : call (int Point.getZ()) { System.out.println(“before call getZ()”); } after() : get (int Point.z) { System.out.println(“after get z”); }
標準処理系 ajc の問題点 コンパイル時 weave 分割コンパイルできない ソースコードが必要 weaver ajc java source aspect コンパイル時 weave 分割コンパイルできない アスペクトを変更したらソースコードも再コンパイルしなければならない ソースコードが必要 サードパーティ製のクラスには使えない 動的ロードに不向き – プログラム時に利用するクラスがわかっていないといけない weaver ajc weaved source weaved bytecode コンパイラ
本研究の提案 : Josh 問題点に対応した新システムJosh を提案 ロード時に,バイトコードレベルで weave java source aspect 問題点に対応した新システムJosh を提案 ロード時に,バイトコードレベルで weave 部分コンパイル結果,バイトコードは一旦ローカルディスクに保存 コンパイラ 部分 コンパイラ Josh bytecode weaver weaved bytecode
Josh の利点 バイトコードレベルでのweave の有用性 ロード時の weave 分割コンパイル可能 アスペクトを変更してもソースの再コンパイルの必要がない ソースコード無しクラスにも適用できる ロード時の weave 複数のアスペクトを切り替えて使える
基本的にはソースコードのあるプログラムのアスペクトを書く ソースコードなしクラスの アスペクト ソースコードを見ないでアスペクトを書けるのか? 汎用性のあるアスペクトなら書ける 分散環境への適応 既存プログラムとは別に分散化のためのアスペクトを記述し,それらをweave して分散プログラムを生成(e.g. Addistant ) 新しいセキュリティ機構の導入 セキュリティチェックコードの挿入 基本的にはソースコードのあるプログラムのアスペクトを書く
バイトコードレベルでの weave の制限 Join-Point の消失 エラーの検出がロード時 コンパイル時にソースコードの情報が失われてしまうことがある 例:インライン展開 エラーの検出がロード時 ソースコードレベルならばコンパイルエラーになるものが、バイトコードレベルではロード時エラーになる 例 : 同じシグネチャのメソッドの追加
Josh の実装 部分コンパイラ Josh Weaver アスペクトをバイトコード化する アスペクトとJava バイトコードをweave する
部分コンパイラ (1) 役割 : ソースコードで存在する,アスペクト内のJavaコード断片をバイトコード化 ダミークラスにコード断片を埋め込んで通常のJavac でコンパイル コード断片だけではコンパイルエラーが起こりうる フィールドやメソッドの参照エラー (上の例では z) int Point.getZ() { return z; } これをバイト コード化したい
部分コンパイラ(2) バイトコード埋め込み先のクラスの, フィールドやメソッドも書き込む リフレクションの機能で調査可能 埋め込み先のクラスのフィールド public class Dummy { int z; int getZ() { return z; } コード断片
Josh Weaver 役割 : アスペクトのバイトコードをロード時にJavaバイトコードに埋め込む Javassist が提供する構造リフレクションを使い実装した プログラムのバイトコードを調べてJoin-Point を見つけ出す バイトコードを編集してアスペクトのバイトコード断片を埋め込む
javassist.CtClass 標準JavaリフレクションAPIと同様に,クラス定義を調べるメソッドを提供 getField(..), getMethod(..), getName(..), getSuperclass(..) さらにクラス定義を変更するメソッドを提供 addField(..), addMethod(..), setSuperclass(..), addInterface(..) Introductionの実現が可能
javassist.CodeConverter 特定のJoin-Point のバイトコードを変換する insertBeforeMethod(..) メソッド呼び出しの前に別メソッドを呼ぶ replaceFieldRead(..) フィールド読み込みを別メソッドに呼び代える CodeConverter の情報をクラスに反映させてバイトコード変換完了 Advice の実現が可能
Weaver のオーバヘッドの測定 実行時間 [ミリ秒] USparcIII 750MHz J2SE 1.3.1 クラスファイルのサイズ [KByte]
まとめ AspectJ 言語の新コンパイラJosh を提案 Josh の weave ロード時 バイトコードレベル 分割コンパイル可能 アスペクト変更による再コンパイルの手間を省ける ソースコードなしクラスにも適用できる サードパーティライブラリもOK 動的ロードにも対応