从抽象语法树获取控制流程图

时间:2020-03-06 14:21:17  来源:igfitidea点击:

我有一个从ANTLR Parser Generator for Java派生的AST。我想做的是以某种方式构造源代码的控制流程图,其中每个语句或者表达式都是唯一的Node。我知道此标识必须具有递归性,我想知道我们建议的最佳选择是什么,以及ANTLR是否具有可以用于此工作的工具集。
干杯,
克里斯

编辑我主要关心的是从AST获得控制流程图(CFG)。这样,我就可以得到源代码的树形表示。为了澄清,源代码和实现语言都是Java。

解决方案

我们曾经尝试过ANTLR Studio吗?它不会生成孔AST图,但对于回顾来说,它已经非常有用。

过去完成此操作后,我使用了graphviz(尤其是点工具)来生成图形。我通过在编译时实际遍历控制流图来创建点输入文件。

图的布局是一个难题,graphviz做得很好。它可以输出为ps,pdf和各种图像格式,并且布局通常看起来非常直观。我强烈推荐它。

根据一些评论,听起来OP确实想进行代码生成-将AST转换为基于基本块和跳转点的较低级指令序列。

代码生成是非常特定于语言的,并且在此主题中进行了大量工作。在进行代码生成之前,我们需要了解目标语言-无论是汇编语言还是其他一些高级语言。一旦确定了这一点,我们只需要遍历AST并生成在AST中实现代码的指令序列。 (我说这很简单,但是可能很难-很难一概而论,因为此处的注意事项是特定于语言的。)

我们选择用于代码生成的表示形式将隐式或者显式包含控制流图。如果目标语言相当低级(接近汇编语言),则控制流图应该相对容易提取。

(如果我们需要更多说明,请发表评论。)

通常,CFG是在较低级别的表示形式(例如JVM字节码)上计算的。几年前有人对这种事情做了论文。其中可能有一个有用的方法描述了如何获得该表示。

由于源语言和目标语言是相同的,因此没有代码生成步骤-我们已经完成了!但是,现在我们可以开始使用AST了。在AST的每个节点上,我们都必须问自己:这是否是"跳跃"指令?方法调用和if语句是跳转指令的示例。循环构造(如" for"和" while")也是如此。诸如加法和乘法之类的指令是不可跳跃的。

首先,将每个Java语句与CFG中的一个节点以及一个入口和出口节点相关联。作为第一近似,走到树上,然后:

  • 如果当前语句是一个方法调用,请找出该方法调用的相应主体的入口节点在哪里,并从当前语句指向该入口节点的边。如果该语句是方法返回值,请枚举可能已调用它的位置,并在这些位置添加一条边。
  • 对于每个非跳转语句,在它与下一个语句之间取一个边。

这将为我们提供某种CFG。该步骤在步骤2中有点繁琐,因为所调用的方法可能在库中声明,而不是在AST的其他地方声明-如果是这样,请不要使边缘或者使代表该库条目的特殊节点边缘方法。

这有意义吗?

生成一个完全考虑所有语言的完整控制流程图
问题比看起来难。我们不仅需要识别什么
似乎是"基本块",但是我们必须确定函数调用
(这很容易,但是确定目标可能会比较困难),
可能发生幕后操作(例如类初始化程序)的地方。
并担心可能发生异常的地方
以及如果确实发生异常,控制权将移至何处。

如果我们仔细检查大多数语言,它们也会
明确表达式中计算值的排序,
如果我们在一个表达式中有两个副作用,这一点就很重要;
控制流应反映顺序(或者非顺序,
(如果未定义)。

也许我们只想要控制流的抽象
具有基本的块和条件。那是
显然容易一些。

无论哪种情况(简单CFG或者完整CFG),我们都需要使用AST,
在每个点上都参考可能的控制流目标
(例如,在大多数情况下,例如IF语句,有两个流目标:
THEN和ELSE子句)。在每个节点上,将该节点链接到
适当的控制流量目标,可能会替换流量目标
(例如,遇到IF时)。

对于Java(或者C)的完整语言语义而言,做到这一点是相当可观的。
大量的工作。我们可能只想使用一个计算该值的工具
现成的。
参见http://www.semanticdesigns.com/Products/DMS/FlowAnalysis.html
从我们的工具中获得真正的效果。