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コンパイラの挙動が厳密に定義されていないため、下手に最適化かけられたりすると動かなくなるかも。