pmd其实有简单的数据流,但是网上搜了下,没有什么文章

寻找方法

查看pmd文档其中有一段介绍data flow analysis的文字

image-20200613141004035

从中可以推断pmd通过net.sourceforge.pmd.SourceCodeProcessor来遍历AST建立CFG和数据流,寻找这个类中进行DFA的地方

image-20200613141517191

可以看到调用了userDFA,最终会找到一个类net.sourceforge.pmd.lang.java.dfa.DataFlowFacade

image-20200613141946339

在这个类中,对方法定义跟构造函数定义进行了处理,接下来就只对方法定义的处理过程做说明

处理过程

net.sourceforge.pmd.lang.java.dfa.StatementAndBraceFinder中,定义了几种AST节点的数据流建立规则。在阅读之前,需要了解一些其他类做的工作

数据结构

net.sourceforge.pmd.lang.dfa.Structure中,定义了三个变量

image-20200613144609560

dataFlow用于存取Node

image-20200613145253385

braceStack包含对数据流有影响的点,cbrStack包含continue, break, return节点。至于分为两个栈来处理,是因为处理continue, break, return的过程与一般点不一样

image-20200613145605891

NodeType与序列

net.sourceforge.pmd.lang.dfa.SequenceChecker中,定义了不同点的控制类型(?)以及先后关系

image-20200613145928461

并对braceStack中的点进行分割,简化描述为从0开始,寻找第i个isLastStep的点,从0-i为一组分割

遍历AST

回到StatementAndBraceFinder

image-20200613151950432

ASTMethodDeclaration中的点进行遍历,这里只对if过程进行说明

image-20200613152534374

image-20200613160746913

这是个if的伪代码,根据代码,会取ASTExpression,判断父节点类型是否为ASTIfStatement,是则创建一个dataflow node,加入dataflow的list中,并打上IF_EXPR的标签,压入braceStack中

image-20200613152845622

image-20200613160925656

继续遍历AST就会取到ASTStatement,中间的super.visit是遍历ASTStatement的子节点,为了处理嵌套的情况,这里就先不管了。然后就会判断这个ASTStatement的父节点是不是ASTIfStatement,如果是,就判断ASTIfStatement是否存在else,如果没有else,就将ASTStatement里最后一个ASTStatementExpression(上述代码的情况下)打上IF_LAST_STATEMENT_WITHOUT_ELSE的标签压入braceStack中

image-20200613153247983

如果存在else,并且当前Statement下标为1,说明是if的body,给最后一个ASTStatementExpression打上IF_LAST_STATEMENT的标签,压入braceStack

image-20200613161018658

如果当前Statement下标不为1,说明是else的body,给最后一个ASTStatementExpression打上ELSE_LAST_STATEMENT的标签,压入braceStack

建立路径

第一种是在把createNewNode时,会创建路径,就是上一个添加的点指向当前创建的点

image-20200615145040104

第二种是根据具体的控制语句,修正路径

image-20200615140518259

对braceStack进行分割,找到第一组firstIndex和lastIndex

image-20200615140614860

如果第一个对象为IF_EXPR,计算是否存在else,对应两种不同的建立方式,这里就以3个参数的computeIf说明

image-20200615145200577

各个点这里就用一张图来解释

image-20200615150400661

如果存在if-body,就将原先if-body(ifEnd)else-body(elseStart)的路径删除,增加if-body(ifEnd)end的路径,再增加ifStartelse-body(elseStart)的路径

如果if-body不存在(ifEnd不为StatementExpression),这时ifStartelseStart的路径存在,只需增加ifStartend的路径即可

如果else-body不存在(elseStart不为StatementExpression),ifEndend的路径存在,只需增加ifStartend的路径即可

以第一种为例,计算结束后,路径应该为

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
+---------+ifStart
|             +
|             |
|             |
|             |
|             |
|             v
|           ifEnd+--------------+
|                               |
|                               |
+---> elseStart & elseEnd       |
              +                 |
              |                 |
              |                 |
              v                 |
             end <--------------+