Ответ 1
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, найти поле и проверить, не является ли он изменчивым.
Проверка синхронизации возможна, но может оказаться сложной из-за нескольких условий 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();
}