前面的博客也提到过,Spotbugs 里面 Detector2 与 Detector,FindBugs2 与 FindBugs,GUI2与GUI,可以视为 Spotbugs 与 FindBugs 新老技术的碰撞,那么本篇着重浏览一下 Detector 是如何进行代码扫描生成报告的。
前面也提到过,Spotbugs 很多地方应用了设计模式,在 Detector 中 visitor 模式更为普遍。而且 Spotbugs 是针对于字节码的检测器。看一下代码结构中的细节:
- apache commons-bcel 字节码工程库(Apache Commons BCEL™)旨在为用户提供一种方便的方法来分析、创建和操作(二进制)Java类文件(以.class结尾的文件)。类由包含给定类的所有符号信息的对象表示:特别是方法、字段和字节码指令。
- BetterVisitor 对 BCEL 库的 visitor做了更为细致的扩展。
- PreorderVisitor 使使用访问者模式编程风格成为可能。也就是说,实现此接口的类只需调用所有类都具有的“accept”方法,就可以遍历Java类的内容。
- AnnotationVisitor 可以访问 类、字段、方法和方法参数上的注解。
- DismantleBytecode 关键类,拆解字节码的类。这个类实现了visit(Code)方法,并为每一个操作码调用sawOpcode(int)方法。
- BytecodeScanningDetector 继承 DismantleBytecode 的基类,
- OpcodeStackDetector 要扫描方法字节码或者使用操作码栈 Detector 基类。 重要
OpcodeStackDetector
使用Stack-based来实现findbugs的检测器都要继承OpcodeStackDetector这个类,并且实现sawOpcode(int)方法。这个方法传入的操作码的值,根据这个值我们可以得到操作数的信息,如操作码是函数调用,则能获取到函数的名称、描述符等信息。另外我们还能获取到方法栈数据,程序计数器等数据,使用这些数据便能实现想要检测的代码模式。
常用套路
- 继承 Base 类
- 创建 BugReporter 的构造方法
- visit 需要的信息,可见 BetterVisit,或者重写 sawOpcode
- 改写逻辑
调用栈
从调用栈上看,ClassContent -> JavaClass -> Method -> Code -> Opcode
visit code
字节码遍历过程中,一些变量会重置与重新赋值,如 classConstantOperand
sawOpcode 方法可以看作每行字节码做了什么操作,参数 seen 可以理解为哪一行。
Constants.INVOKEVIRTUAL 表示该行调用类的实例方法,
Constants.INVOKESTATIC 表示调用类的静态方法。
getNameConstantOperand 方法表示获取被调用方法的名称,
getClassConstantOperand 方法表示获取调用类的名称,
getSigConstantOperand 方法表示获取方法的所有参数。
类检测
方法检测
对于方法的检测,
代码行检测
sawOpcode 可以看做每行字节码的操作