分类:
2008-10-28 18:17:17
编写 RuntimeException 捕获检测器
正如您在 中所学的,编写 bug 模式的第一个步骤是清楚地标识 bug 模式。在这里,bug 模式是捕获 Exception
的 catch
块,这时不存在用于 RuntimeException
的相应捕获块,并且尝试块中的任何方法调用或 throw
语句都不会抛出 Exception
。要检测此 bug 模式,则需要知道 try-catch
块的位置、try
块可能抛出的内容以及在 catch
块中将捕获的内容。
像上个月的操作一样,您可以通过创建 BytecodeScanningDetector
基础类(可实现 Visitor
模式)的子类启动 bug 检测器。在 BytecodeScanningDetector
中有一个 visit(Code)
方法,并且在每次发现 catch
块时,该实现都会调用 visit(CodeException)
。如果重写 visit(Code)
,并从那里调用 super.visit(Code)
,则当超类 visit(Code)
返回时,它将调用用于该方法中所有 catch
块的 visit(CodeException)
。清单 4 了显示实现 visit(Code)
和 visit(CodeException)
的第一步,它将积累方法中所有 catch
块的信息。每个 CodeException
都包含相应 try
块的起始和终止的字节码偏移量,这样您可以方便地确定哪一个 CodeException
对象与 try-catch
块对应。
|
此时,您已获得了您需要的一半信息:在何处捕获哪些异常。现在必须找出哪些异常被抛出。为此,您需要重写 BytecodeScanningDetector
的 sawOpcode()
方法,并处理与方法调用和异常抛出相对应的字节码。可以根据 athrow
JVM 指令抛出异常。三个 JVM 指令分别用于调用以下方法:invokestatic
、invokevirtual
和 invokespecial
。就像使用 visit(CodeException)
一样,在调用超类 visit(Code)
时可以调用 sawOpcode
,这样,如果在 sawOpcode()
中收集信息,那么在 super.visit(Code)
返回时,您将获得您需要的、有关捕获和抛出异常的所有信息。
清单 5 显示了 sawOpcode()
的实现,它将处理上述 JVM 指令。对于 athrow
指令,可以使用 FindBugs 的 OpcodeStack
帮助器类来了解 athrow
操作数的类型。对于方法调用指令,可以使用 Bytecode Engineering Library (BCEL) 类来提取方法声明抛出的已检查异常的类型。在任何一种情况下,都可以积累关于哪些异常在方法中的哪个字节码偏移量被抛出的信息,这样,在完成整个方法的处理后,可以将它们进行匹配。
|
[2]
public void sawOpcode(int seen) {
stack.mergeJumps(this);
try {
switch (seen) {
case ATHROW:
if (stack.getStackDepth() > 0) {
OpcodeStack.Item item = stack.getStackItem(0);
String signature = item.getSignature();
if (signature != null && signature.length() > 0) {
if (signature.startsWith("L"))
signature = SignatureConverter.convert(signature);
else
signature = signature.replace('/', '.');
throwList.add(new ExceptionThrown(signature, getPC()));
}
}
break;
case INVOKEVIRTUAL:
case INVOKESPECIAL:
case INVOKESTATIC:
String className = getDottedClassConstantOperand();
try {
if (!className.startsWith("[")) {
Class clazz = Repository.lookupClass(className);
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (method.getName().equals(getNameConstantOperand())
&& method.getSignature().equals(getSigConstantOperand())) {
ExceptionTable et = method.getExceptionTable();
if (et != null) {
String[] names = et.getExceptionNames();
for (String name : names)
throwList.add(new ExceptionThrown(name, getPC()));
}
break;
}
}
}
} catch (ClassNotFoundException e) {
bugReporter.reportMissingClass(e);
}
break;
default:
break;
}
} finally {
stack.sawOpcode(this, seen);
}
}