之前的章节中都没有考虑方法调用,然而在实际的程序中方法调用非常常见,那么我们如何分析带方法调用的程序呢?最简单的处理方式是(这里仍然以常量传播作为一个例子):做最保守的假设,即为函数调用返回NAC。而这种情况会丢失精度。引入过程间分析能够提高精度。如果使用最简单的处理方式,下图中的n和y分析结果都不是常量,尽管我们能够一眼看出他们的运行时值是n=10,y=43。

A representation of calling relationships in the program.
调用关系图表达调用关系(中文讲起来确实很奇怪),一个简单的例子如下:

Call Graph有很多种不同的构造方法,我们接下来会讲解两个极端:
最准确(Pointer Analysis)和最快速(Class Hierarchy Analysis)。

本课主要关注Java的调用关系图构建。为此,我们需要先了解Java中调用的类型。Java中call可分为三类(不需要理解透彻,之后会详细介绍):

Virtual call是几种调用中最为复杂的一种,我们首先重点讨论它。在动态运行时,Virtual call基于两点决定调用哪个具体方法:
Java中Dispatch机制决定具体调用哪个方法:c是一个类的定义,m是一个方法。如果能在本类中找到name和descriptor一致的方法,则调用c的方法,否则到父类中寻找。
We define function Dispatch(𝑐, 𝑚) to simulate the procedure of run-time method dispatch.
练习问题
Q:两次对foo的调用分别调用了哪个类的foo?

A:分别调用A和C中定义的foo方法。

a.foo()中,a就是接收变量下面介绍解析调用的算法。

Static call 静态调用

Special call 特殊调用


Virtual call

一个例子
三个调用都是Virtual call。是上述算法中的第三种情况。

常用于IDE中,给用户提供提示。比如写一小段测试代码,看看b.foo()可能会调用哪些函数签名。可以看出CHA分析中认为b.foo()可能调用A、C、D中的foo()方法。(实际上这并不准确,因为b实际上是B类对象,不会调用子类C、D中的方法,但胜在快速)







注:忽略new A()对构造函数的调用,这不是例子的重点。
ICFG = CFGs + call & return edges
ICFG可以通过CFG加上两种边构造得到。
例如:


目前这一分析领域没有标准方法。首先对过程间和过程内的分析做一个对比,并以常量传播(本校同学第一次实验作业主题,需要一到六课的基础)为例子进行解释。

Edge transfer处理引入的call & return edge。为此,我们需要在之前章节的CFG基础上增加三种transfer函数。


这一段有存在的必要吗?

Such edge (from call site to return site) is named call-to-return edge. It allows the analysis to propagate local data-flow (a=6 in this case) on ICFG.
如果没有这一段,那么a就得“出国”去浪费地球资源——在分析被调用函数的全程中都需要记住a的值,这在程序运行时会浪费大量内存。

要记得在调用语句处kill掉表达式左边的值,否则会造成结果的不准确,如:

讲到这里,我们回到故事的开头,看看过程间分析的引入到底能带来多大的精度提高吧。上述例子应用过程间分析的完整推导如下:

而如果只做过程内分析,则精度大大下降:

之前的章节中都没有考虑方法调用,然而在实际的程序中方法调用非常常见,那么我们如何分析带方法调用的程序呢?最简单的处理方式是(这里仍然以常量传播作为一个例子):做最保守的假设,即为函数调用返回NAC。而这种情况会丢失精度。引入过程间分析能够提高精度。如果使用最简单的处理方式,下图中的n和y分析结果都不是常量,尽管我们能够一眼看出他们的运行时值是n=10,y=43。

A representation of calling relationships in the program.
调用关系图表达调用关系(中文讲起来确实很奇怪),一个简单的例子如下:

Call Graph有很多种不同的构造方法,我们接下来会讲解两个极端:
最准确(Pointer Analysis)和最快速(Class Hierarchy Analysis)。

本课主要关注Java的调用关系图构建。为此,我们需要先了解Java中调用的类型。Java中call可分为三类(不需要理解透彻,之后会详细介绍):

Virtual call是几种调用中最为复杂的一种,我们首先重点讨论它。在动态运行时,Virtual call基于两点决定调用哪个具体方法:
Java中Dispatch机制决定具体调用哪个方法:c是一个类的定义,m是一个方法。如果能在本类中找到name和descriptor一致的方法,则调用c的方法,否则到父类中寻找。
We define function Dispatch(𝑐, 𝑚) to simulate the procedure of run-time method dispatch.
练习问题
Q:两次对foo的调用分别调用了哪个类的foo?

A:分别调用A和C中定义的foo方法。

a.foo()中,a就是接收变量下面介绍解析调用的算法。

Static call 静态调用

Special call 特殊调用


Virtual call

一个例子
三个调用都是Virtual call。是上述算法中的第三种情况。

常用于IDE中,给用户提供提示。比如写一小段测试代码,看看b.foo()可能会调用哪些函数签名。可以看出CHA分析中认为b.foo()可能调用A、C、D中的foo()方法。(实际上这并不准确,因为b实际上是B类对象,不会调用子类C、D中的方法,但胜在快速)







注:忽略new A()对构造函数的调用,这不是例子的重点。
ICFG = CFGs + call & return edges
ICFG可以通过CFG加上两种边构造得到。
例如:


目前这一分析领域没有标准方法。首先对过程间和过程内的分析做一个对比,并以常量传播(本校同学第一次实验作业主题,需要一到六课的基础)为例子进行解释。

Edge transfer处理引入的call & return edge。为此,我们需要在之前章节的CFG基础上增加三种transfer函数。


这一段有存在的必要吗?

Such edge (from call site to return site) is named call-to-return edge. It allows the analysis to propagate local data-flow (a=6 in this case) on ICFG.
如果没有这一段,那么a就得“出国”去浪费地球资源——在分析被调用函数的全程中都需要记住a的值,这在程序运行时会浪费大量内存。

要记得在调用语句处kill掉表达式左边的值,否则会造成结果的不准确,如:

讲到这里,我们回到故事的开头,看看过程间分析的引入到底能带来多大的精度提高吧。上述例子应用过程间分析的完整推导如下:

而如果只做过程内分析,则精度大大下降:
