Chris Grindstaff は、FindBugs、パート 2: カスタム ディテクタの作成という記事を書き、 BCELを使用して独自のルールを追加する方法を説明しています。(BCEL は唯一のバイトコード ライブラリではありませんが、FindBugs が使用するライブラリです。)
以下のコードは、メソッドが静的メソッドまたはフィールドにアクセスするすべてのケースを出力します。Runnableを実装する任意の型で実行できます。
public class StaticInvocationFinder extends EmptyVisitor {
@Override
public void visitMethod(Method obj) {
System.out.println("==========================");
System.out.println("method:" + obj.getName());
Code code = obj.getCode();
InstructionList instructions = new InstructionList(code.getCode());
for (Instruction instruction : instructions.getInstructions()) {
// static field or method
if (Constants.INVOKESTATIC == instruction.getOpcode()) {
if (instruction instanceof InvokeInstruction) {
InvokeInstruction invokeInstruction = (InvokeInstruction) instruction;
ConstantPoolGen cpg = new ConstantPoolGen(obj
.getConstantPool());
System.out.println("static access:"
+ invokeInstruction.getMethodName(cpg));
System.out.println(" on type:"
+ invokeInstruction.getReferenceType(cpg));
}
}
}
instructions.dispose();
}
public static void main(String[] args) throws Exception {
JavaClass javaClass = Repository.lookupClass("StopThread$1");
StaticInvocationFinder visitor = new StaticInvocationFinder();
DescendingVisitor classWalker = new DescendingVisitor(javaClass,
visitor);
classWalker.visit();
}
}
このコードは以下を発行します。
==========================
method:<init>
==========================
method:run
static access:access$0
on type:StopThread
次に、タイプStopThreadをスキャンし、フィールドを見つけて、それがvolatileかどうかを確認することができます。
同期のチェックは可能ですが、複数の MONITOREXIT 条件が原因で複雑になる可能性があります。呼び出しスタックをたどることも難しいかもしれませんが、これは些細な問題ではありません。ただし、一貫して実装されていれば、バグパターンをチェックするのは比較的簡単だと思います。
BCELifierクラスが見つかるまで、BCEL はほとんど文書化されておらず、非常に複雑に見えます。クラスで実行すると、BCEL でクラスを構築する方法の Java ソースが吐き出されます。これをStopThreadで実行すると、 access$0合成アクセサーを生成するために次のようになります。
private void createMethod_2() {
InstructionList il = new InstructionList();
MethodGen method = new MethodGen(ACC_STATIC | ACC_SYNTHETIC, Type.BOOLEAN, Type.NO_ARGS, new String[] { }, "access$0", "StopThread", il, _cp);
InstructionHandle ih_0 = il.append(_factory.createFieldAccess("StopThread", "stopRequested", Type.BOOLEAN, Constants.GETSTATIC));
il.append(_factory.createReturn(Type.INT));
method.setMaxStack();
method.setMaxLocals();
_cg.addMethod(method.getMethod());
il.dispose();
}