Крис Гриндстафф написал статью 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. Ходить по стекам вызовов тоже может быть сложно, но тогда это не тривиальная проблема. Тем не менее, я думаю, что было бы относительно легко проверить шаблон ошибки, если бы он был реализован последовательно.
BCEL выглядит скудно задокументированным и очень волосатым, пока не найдете класс BCELifier . Если вы запускаете его в классе, он показывает исходный код Java о том, как построить класс в BCEL. Запуск его на 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();
}