Reachの解析

ふと思った。どうしてバイトコードエンジニアリングでcall-by-need的なチューニングするフレームワークがないんだろう。

LOG.debug(MessageFormat.format(...));


Javaはcall-by-valueなので、LOG.debugの起動に先立って引数を評価してしまう。そのチューニングのためにif文で囲んで必要に応じて引数を遅延評価するようなプログラム(うろ覚え)

if (LOG.isDebug()) {
  LOG.debug(MessageFormat.format("{0}, {1}", hoge, foo));
}

みたいなコードを吐き出すくらいしてくれてもいいんじゃないかと。

と思ったけど、バイトコードエンジニアリングだと大変なのね。バイトコードの命令セット考えると、おそらく

Logging l0;
String s1;
Object o2, o3;
l0 = LOG;
s1 = "{0}, {1}";
o2 = hoge;
o3 = foo;
o2 = new Object[]{ o2, o3 };
s1 = MessageFormat.format(s1, o2);
l0.debug(s1);

みたいなバイトコード作られちゃってて、それに対してLogging.debugの起動を拾ってcall-by-needにしても

Logging l0;
String s1;
Object o2, o3;
l0 = LOG;
s1 = "{0}, {1}";
o2 = hoge;
o3 = foo;
o2 = new Object[]{ o2, o3 };
s1 = MessageFormat.format(s1, o2); // 評価されちゃってる
if (LOG.isDebug()) { // 手遅れ気味
    l0.debug(s1);
}

みたいな残念なバイトコードを吐く予感。

これは単にLogging.debugの引数に最終的に利用されるバイトコードすべてを検出して、それ全体を操作すればよさげ。再帰的にオペランドスタックのreachを解析すればいいだけなので、それほど激しくないはず。ただし、今のJavaコンパイラの挙動が厳密に定義されていないため、下手に最適化かけられたりすると動かなくなるかも。