Programming Assignment 6

作业 6:上下文敏感的指针分析

南京大学《软件分析》课程
作业出品:谭添、李樾

1 作业导览

  • 为 Java 实现一个上下文敏感的指针分析框架。
  • 作为指针分析的一部分,随着指针分析一起实现调用图(call graph)构建。
  • 实现几种常见的上下文敏感策略(context sensitivity variants)。

在本次实验作业中,你将会在 Tai-e 上实现一个上下文敏感的 Java 指针分析框架,其中程序调用图的构建是伴随着指针分析一同进行的。另外,你还需要实现几种具体的上下文敏感策略。在实现正确的情况下,上下文敏感的指针分析构建的调用图将会比使用非上下文敏感的指针分析(CIPTA)构建的调用图更精确。此外,不同的上下文敏感策略会对精度产生不同的影响,这一点后面会继续讨论。

和上一次作业类似,在本次作业中,我们将会在上下文敏感的语境下处理课件中未覆盖到的一些 Java 语言特性(包括静态成员变量,数组和静态方法)。因此,你实现的上下文敏感指针分析能够处理 Java 中各种类型的指针。

在阅读接下来的内容前,请先按照 Tai-e 框架(教学版)配置指南 将作业 6 对应的 Tai-e 项目(位于实验作业仓库的 A6/tai-e/ )配置好。

2 实现上下文敏感指针分析

2.1 分析范围

你需要实现第 11 和 12 讲介绍的上下文敏感指针分析算法,这一算法处理两类指针(即局部变量和实例字段(instance field))和实例方法的调用。为了实现一个更加实用的指针分析,你需要额外处理两类指针(即静态字段(static field)和数组索引(array index))以及静态方法的调用。处理以上新特性的分析规则将在第 2.2 节介绍。这些新规则和你在课上学过的规则十分类似,甚至更为简单,你需要做的就是弄清楚怎么在指针分析算法中实现这些规则。

2.2 新分析规则

在这一节,我们介绍新引入的用于处理静态字段、数组索引和静态方法调用的上下文敏感指针分析规则。

静态字段. 静态字段的处理方式较为简单:在静态字段和带上下文的变量之间传递值即可。我们用 T.f 来表示静态字段 T.f,并定义如下规则来处理静态字段的 store 和 load:

类型语句规则(在上下文 c 下)PFG 边
Static StoreT.f = yc:oipt(c:y)
c:oipt(T.f)
T.fc:y
Static Loady = T.fc:oipt(T.f)
c:oipt(c:y)
c:yT.f

数组索引. 我们在第 8 讲中提到过,常规的指针分析不区别对针对同一数组不同位置的 store 和 load。假设 c:oi 代表一个具有上下文 c 的数组对象,那么我们用 c:oi[] 来表示一个指针,指向所有保存在该数组中的对象(无论保存在什么位置)。根据这一处理方式,我们定义如下用来处理数组 store 和 load 的规则:

类型语句规则(在上下文 c 下)PFG 边
Array Storex[i] = yc:oupt(c:x),c:ovpt(c:y)
c:ovpt(c:ou[])
c:ou[]c:y
Array Loady = x[i]c:oupt(c:x),c:ovpt(c:ou[])
c:ovpt(c:y)
c:yc:ou[]

静态方法. 在上下文敏感指针分析中,对静态方法的处理和对实例方法的处理有两点区别:1)不需要在 receiver object 上通过 dispatch 来解析出被调用方法。2)不需要传递 receiver object。由于静态方法调用不需要 receiver object,其处理规则比实例方法调用更为简单:

类型语句规则(在上下文 c 下)PFG 边
Static Calll: r = T.m(a1,...,an)ct= Select(c,l)
c:oupt(c:aj),1jn
c:ovpt(ct:mret)
c:oupt(ct:mpj),1jn
c:ovpt(c:r)
c:a1ct:mp1

c:anct:mpn
c:rct:mret

2.3 Tai-e 中你需要了解的类

本作业中,你将会用到之前作业中介绍过的一些类,具体而言,为了实现上下文敏感指针分析,你需要用到和之前作业中一样的IR相关类:IRVarInvokeExp 以及 DefinitionStmt 的如下子类:

Subclasses of DefinitionStatement

此外,JMethodJFieldObjHeapModel 也会在本次作业中被用到。

下面我们介绍本次作业,即上下文敏感的指针分析特有的一些类。总的来说这些类都比较简单,并且大部分都和上一次作业的对应类很相似,区别是本次它们包含了上下文所需的相关信息。你需要阅读这些类的源码和注释并自行决定如何使用它们。

  • pascal.taie.analysis.pta.core.cs.context.Context

    该类表示上下文敏感的指针分析中的上下文。

  • pascal.taie.analysis.pta.core.cs.element.CSElement

    该类表示指针分析中需要用到上下文的元素,每个这样的元素都和一个上下文相关联。它有四个子类,分别表示四种需要用到上下文的元素:

    Subclasses of CSElement
    • CSVar:表示一个带上下文(Context)的变量(Var)。
    • CSObj:表示一个带上下文(Context)的抽象对象(Obj)。
    • CSCallSite:表示一个带上下文(Context)的调用点(Invoke)。
    • CSMethod:表示一个带上下文(Context)的方法(JMethod)。
  • pascal.taie.analysis.pta.core.cs.element.Pointer

    该类表示上下文敏感指针分析中的指针,即 PFG(pointer flow graph)中的节点。与 *.ci.Pointer(前一次作业中使用的类)类似,每个 Pointer 和一个 PointsToSet 相关联,该 PointsToSet 可以通过调用 getPointsToSet() 取得。该类也有四个子类:

    Subclasses of Pointer

    这四个子类分别对应了我们在第 8 讲课件第 82 页中介绍过的(在上下文敏感语境下的)Java中四种不同指针。

  • pascal.taie.analysis.pta.core.cs.element.CSManager

    该类管理所有需要用到上下文的元素和所有上下文敏感的指针。也就是说,CSElementPointer 的所有子类的实例(每当你需要用到时)都需要通过这个类的相关 API 来取得。

  • pascal.taie.analysis.pta.core.cs.selector.ContextSelector

    该类是上下文敏感指针分析框架和具体的上下文敏感策略(如调用点敏感、对象敏感等)之间的接口。该类有 4 个 API,其中一个 API 返回空上下文,另外三个 API 分别为静态方法、实例方法和堆对象(heap object)选择上下文。本次作业中,当你在上下文敏感指针分析中处理 InvokeNew 语句时,你需要使用本类的各个子类中的方法来生成目标方法(Invoke 语句)和新创建对象(new 语句)的上下文。你需要完成本类的六个子类中的方法(在第 3 节中进一步介绍)。

  • pascal.taie.analysis.pta.core.cs.CSCallGraph

    该类表示上下文敏感的调用图,它和你上一次作业用的 DefaultCallGraph 非常类似,区别仅为现在调用点和方法被表示为 CSCallSiteCSMethod。你需要使用其中的 addReachableMethod(CSMethod) 以及 addEdge(Edge) 这两个 API 来修改调用图。

  • pascal.taie.analysis.pta.pts.PointsToSet

    该类表示指针集(points-to sets),即上下文敏感指针分析中 CSObj 的集合。该类是可迭代的,这意味着你可以用一个 for 循环来迭代指针集中的对象:

    PointsToSet pts = ...;
    for (CSObj obj : pts) {
        ...
    }
    
  • pascal.taie.analysis.pta.pts.PointsToSetFactory

    该类提供创建 PointsToSet 的静态工厂方法,你可以用该类中的两个 make() 方法来创建 PointsToSet 的实例。

  • pascal.taie.analysis.pta.cs.PointerFlowGraph

    该类表示程序的指针流图 PFG。

  • pascal.taie.analysis.pta.cs.WorkList

    该类表示指针分析算法中的 WorkList。

  • pascal.taie.analysis.pta.cs.Solver

    该类实现了一个上下文敏感的指针分析求解器。它的代码并不完整,你需要完成它(在第 2.4 节中进一步说明)。

2.4 你的任务 [重点!]

你的第一个任务是完成上下文敏感指针分析框架的核心实现,即 Solver 类的实现。Solver 类中已经有部分实现好的代码:Solver 类的构造方法中包含了设置堆模型(heap model)和 context selector 的相关代码;Solver.initialize() 方法中包含了初始化 context sensitivity (C.S.) manager、worklist、指针流图和调用图并将它们 store 到 Solver 的字段中的相关代码,从而你可以直接使用这些字段。和上次作业一样,你需要完成以下 5 个API:

  • void addReachable(CSMethod)

    该方法实现了第 12 讲的课件中第 88 页上的 AddReachable 函数。

    提示

    1. 和上一次作业相同,在本方法中请别忘了处理静态字段的 store/load 以及静态方法的调用。另外,本次作业中,我们一样提供了方法 Solver.resolveCallee(CSObj,Invoke) 用于解析各种方法调用的被调用方法。
    2. 在本次作业中,你也同样可以在 addReachable() 方法中用访问者模式open in new window处理不同类型的语句。在上下文敏感指针分析中,访问者模式的具体访问者(内部类 StmtProcessor)的 visit(...) 方法需要能够访问到正在被处理的 CSMethodContext,因此我们为 StmtProcessor 的构造方法添加了一个 CSMethod 参数。在 addReachable() 方法中,你需要为每个可达的 CSMethod 创建一个 StmtProcessor 的实例来处理该方法中的语句。如果你对于访问者模式不熟悉,你也可以用任何你喜欢的方式来完成 addReachable() 方法。
  • void addPFGEdge(Pointer,Pointer)

    该方法实现了第 12 讲的课件中第 94 页上的 AddEdge 函数。

  • void analyze()

    该方法实现了第 12 讲的课件中第 88 页上的 Solve 函数的主要部分,即 while 循环的部分。

    提示: 你需要在本方法中处理数组的 store/load。

  • PointsToSet propagate(Pointer,PointsToSet)

    和上一次作业一样,该方法包括了算法中的两个步骤。该方法首先计算两个指针集的差集(Δ=ptspt(n)),然后按照第 12 讲的课件中第 94 页上的 Propagate 函数,将 pts 传播到 pt(n) 中。最后该方法返回 Δ 作为调用结果。

  • void processCall(CSVar,CSObj)

    该方法实现了第 12 讲的课件中第 88 页上的 processCall 函数。完成该方法的提示和上一次作业一样。

我们已经为上面的 API 提供了部分实现代码,你的任务是完成代码中被 “TODO – finish me” 注释的部分。

3 实现常见的上下文敏感策略

3.1 分析范围

本次作业中,你需要实现你在第 12 讲中学到的三种常见的上下文敏感策略,即调用点敏感(call-site sensitivity)、对象敏感(object sensitivity)和类型敏感(type sensitivity)。对每个策略,你需要分别实现两个 selector(分别针对 k-limiting 的上下文中的 k=1k=2)。

3.2 Tai-e 中你需要了解的类

  • pascal.taie.analysis.pta.core.cs.selector.ContextSelector

    该类在第 2.3 节中已经介绍过,本次作业中包括了该类的七个子类:

    Subclasses of ContextSelector

    其中 CISelector 有完整的代码,它实现了非上下文敏感策略。另外六个 selector(红色框中的)的代码是不完整的,在本此作业中你需要完成它们(第 3.3 节进一步介绍)。

  • pascal.taie.analysis.pta.core.cs.context.ListContext

    该类实现了 Context 接口,它将每个上下文表示为一个由若干同类型元素组成的有序列表(对三种上下文敏感策略,该列表分别采用不同的元素来表示上下文:调用点敏感使用的元素为 Invoke,对象敏感使用的元素为 Obj,类型敏感使用的元素为 Type)。该类提供一系列静态工厂方法,即 make(...) 方法来创建上下文,你需要利用这些方法来完成上面提到的六个上下文 selector。

3.3 你的任务 [重点!]

你的第二个任务是实现三种常见的上下文敏感策略。具体而言,你需要完成六个 context selector ,每个 selector 中都需要完成三个 API:两个 selectContext(...) 方法和一个 selectHeapContext(...) 方法。

我们在本次作业中这样设计是为了保持作业的简单性。事实上,Tai-e 为不同的上下文敏感策略提供可任意配置层数的上下文selector。

对每个 k 层的 context selector,其堆上下文(heap context)的层数为 k1,举例来说,对一层调用点敏感(1-call-site sensitivity),堆上下文的层数为 0(即没有堆上下文);对两层调用点敏感(2-call-site sensitivity),堆上下文的层数为 1。

为了给方法选择上下文,你需要为每个 selector 实现:

  • Context selectContext(CSCallSite,JMethod)
  • Context selectContext(CSCallSite,CSObj,JMethod)

为了选择堆上下文,你需要为每个 selector 实现:

  • Context selectHeapContext(CSMethod,Obj)

下面我们列出了六个你需要完成的selector。

  • pascal.taie.analysis.pta.core.cs.selector._1CallSelector

    该类实现了一层调用点敏感。

  • pascal.taie.analysis.pta.core.cs.selector._1ObjSelector

    该类实现了一层对象敏感。

  • pascal.taie.analysis.pta.core.cs.selector._1TypeSelector

    该类实现了一层类型敏感。

  • pascal.taie.analysis.pta.core.cs.selector._2CallSelector

    该类实现了两层调用点敏感。

  • pascal.taie.analysis.pta.core.cs.selector._2ObjSelector

    该类实现了两层对象敏感。

  • pascal.taie.analysis.pta.core.cs.selector._2TypeSelector

    该类实现了两层类型敏感。

  • 提示

    1. 对于如何实现调用点、对象和类型敏感,请分别参考第 12 讲的课件中的第 103、144 和 171 页。
    2. 在调用点敏感中,对静态方法选取上下文的规则和实例方法的相同,即,对于一个静态方法调用,我们组合调用者方法的上下文与调用点本身,来构成被调用方法(本次静态方法调用的目标方法)的上下文。在对象敏感和类型敏感中,处理静态方法调用时简单直接地使用调用者方法的上下文作为被调用方法的上下文。本次作业中你需要用上述规则来为静态方法调用选取上下文。
    3. 方法 selectContext(...)selectHeapContext(...) 的最后一个参数在本作业中未被使用。

    我们已经为上面的类提供了部分实现代码,你的任务是完成代码中被 “TODO – finish me” 注释的部分

4 运行与测试

你可以按我们在 Tai-e 框架(教学版)配置指南 中提到的方式来运行分析。在本作业中,Tai-e 为输入程序执行上下文敏感的指针分析,并输出各类指针的指针集和程序的调用图。

--------------Pointer analysis statistics:--------------
#var pointers: 0 (insens) / 0 (sens)
#var points-to: 0 (insens) / 0 (sens)
#static field points-to: 0 (sens)
#instance field points-to: 0 (sens)
#array points-to: 0 (sens)
#reachable methods: 0 (insens) / 0 (sens)
#call graph edges: 0 (insens) / 0 (sens)
----------------------------------------
Points-to sets of all variables
Points-to sets of all static fields
Points-to sets of all instance fields
Points-to sets of all array indexes
#reachable methods: 0
----------Reachable methods:----------
#call graph edges: 0
----------Call graph edges:----------
----------------------------------------

在你还未完成本作业的时候,输出的指针集和调用图是空的,当你完成了所有空缺代码后,分析的输出应当形如:

--------------Pointer analysis statistics:--------------
#var pointers: 19 (insens) / 19 (sens)
#var points-to: 31 (insens) / 31 (sens)
#static field points-to: 0 (sens)
#instance field points-to: 4 (sens)
#array points-to: 0 (sens)
#reachable methods: 8 (insens) / 8 (sens)
#call graph edges: 11 (insens) / 11 (sens)
----------------------------------------
Points-to sets of all variables
[]:<A: B get()>/%this -> [[]:NewObj{<OneObject: void m()>[0@L7] new A}]
[]:<A: B get()>/temp$0 -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}, []:NewObj{<OneObject: void m()>[9@L10] new B}]
[]:<A: void <init>()>/%this -> [[]:NewObj{<OneObject: void m()>[0@L7] new A}, []:NewObj{<OneObject: void m()>[3@L8] new A}]
[]:<A: void doSet(B)>/%this -> [[]:NewObj{<OneObject: void m()>[0@L7] new A}, []:NewObj{<OneObject: void m()>[3@L8] new A}]
[]:<A: void doSet(B)>/p -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}, []:NewObj{<OneObject: void m()>[9@L10] new B}]
[]:<A: void set(B)>/%this -> [[]:NewObj{<OneObject: void m()>[0@L7] new A}, []:NewObj{<OneObject: void m()>[3@L8] new A}]
[]:<A: void set(B)>/b -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}, []:NewObj{<OneObject: void m()>[9@L10] new B}]
[]:<B: void <init>()>/%this -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}, []:NewObj{<OneObject: void m()>[9@L10] new B}]
[]:<OneObject: void m()>/a1 -> [[]:NewObj{<OneObject: void m()>[0@L7] new A}]
[]:<OneObject: void m()>/a2 -> [[]:NewObj{<OneObject: void m()>[3@L8] new A}]
[]:<OneObject: void m()>/b1 -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}]
[]:<OneObject: void m()>/b2 -> [[]:NewObj{<OneObject: void m()>[9@L10] new B}]
[]:<OneObject: void m()>/temp$0 -> [[]:NewObj{<OneObject: void m()>[0@L7] new A}]
[]:<OneObject: void m()>/temp$1 -> [[]:NewObj{<OneObject: void m()>[3@L8] new A}]
[]:<OneObject: void m()>/temp$2 -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}]
[]:<OneObject: void m()>/temp$3 -> [[]:NewObj{<OneObject: void m()>[9@L10] new B}]
[]:<OneObject: void m()>/temp$4 -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}, []:NewObj{<OneObject: void m()>[9@L10] new B}]
[]:<OneObject: void m()>/x -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}, []:NewObj{<OneObject: void m()>[9@L10] new B}]
[]:<java.lang.Object: void <init>()>/%this -> [[]:NewObj{<OneObject: void m()>[0@L7] new A}, []:NewObj{<OneObject: void m()>[3@L8] new A}, []:NewObj{<OneObject: void m()>[6@L9] new B}, []:NewObj{<OneObject: void m()>[9@L10] new B}]
Points-to sets of all static fields
Points-to sets of all instance fields
[]:NewObj{<OneObject: void m()>[0@L7] new A}.f -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}, []:NewObj{<OneObject: void m()>[9@L10] new B}]
[]:NewObj{<OneObject: void m()>[3@L8] new A}.f -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}, []:NewObj{<OneObject: void m()>[9@L10] new B}]
Points-to sets of all array indexes
#reachable methods: 8
----------Reachable methods:----------
<A: B get()>
<A: void <init>()>
<A: void doSet(B)>
<A: void set(B)>
<B: void <init>()>
<OneObject: void m()>
<OneObject: void main(java.lang.String[])>
<java.lang.Object: void <init>()>
#call graph edges: 11
----------Call graph edges:----------
<A: void <init>()>[0@L17] invokespecial %this.<java.lang.Object: void <init>()>(); -> [<java.lang.Object: void <init>()>]
<A: void set(B)>[0@L21] invokevirtual %this.<A: void doSet(B)>(b); -> [<A: void doSet(B)>]
<B: void <init>()>[0@L33] invokespecial %this.<java.lang.Object: void <init>()>(); -> [<java.lang.Object: void <init>()>]
<OneObject: void m()>[1@L7] invokespecial temp$0.<A: void <init>()>(); -> [<A: void <init>()>]
<OneObject: void m()>[4@L8] invokespecial temp$1.<A: void <init>()>(); -> [<A: void <init>()>]
<OneObject: void m()>[7@L9] invokespecial temp$2.<B: void <init>()>(); -> [<B: void <init>()>]
<OneObject: void m()>[10@L10] invokespecial temp$3.<B: void <init>()>(); -> [<B: void <init>()>]
<OneObject: void m()>[12@L11] invokevirtual a1.<A: void set(B)>(b1); -> [<A: void set(B)>]
<OneObject: void m()>[13@L12] invokevirtual a2.<A: void set(B)>(b2); -> [<A: void set(B)>]
<OneObject: void m()>[14@L13] temp$4 = invokevirtual a1.<A: B get()>(); -> [<A: B get()>]
<OneObject: void main(java.lang.String[])>[0@L3] invokestatic <OneObject: void m()>(); -> [<OneObject: void m()>]
----------------------------------------

注意,上面的输出是非上下文敏感分析的结果,你可以配置 Tai-e 来使用其他你实现的上下文敏感策略。你只需要修改 tai-e/ 目录下的 plan.yml 文件的第三行,即cs:ci这一行,并把 ci 改成下面中的一个:

  • ci:非上下文敏感(已实现好且可以直接使用)
  • 1-call:一层调用点敏感
  • 1-obj:一层对象敏感
  • 1-type:一层类型敏感
  • 2-call:两层调用点敏感
  • 2-obj:两层对象敏感
  • 2-type:两层类型敏感

例如,如果你设置为cs: 1-obj,那么输出应当形如:

--------------Pointer analysis statistics:--------------
#var pointers: 19 (insens) / 28 (sens)
#var points-to: 28 (insens) / 28 (sens)
#static field points-to: 0 (sens)
#instance field points-to: 2 (sens)
#array points-to: 0 (sens)
#reachable methods: 8 (insens) / 15 (sens)
#call graph edges: 11 (insens) / 14 (sens)
----------------------------------------
Points-to sets of all variables
[NewObj{<OneObject: void m()>[0@L7] new A}]:<A: B get()>/%this -> [[]:NewObj{<OneObject: void m()>[0@L7] new A}]
[NewObj{<OneObject: void m()>[0@L7] new A}]:<A: B get()>/temp$0 -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}]
[NewObj{<OneObject: void m()>[0@L7] new A}]:<A: void <init>()>/%this -> [[]:NewObj{<OneObject: void m()>[0@L7] new A}]
[NewObj{<OneObject: void m()>[0@L7] new A}]:<A: void doSet(B)>/%this -> [[]:NewObj{<OneObject: void m()>[0@L7] new A}]
[NewObj{<OneObject: void m()>[0@L7] new A}]:<A: void doSet(B)>/p -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}]
[NewObj{<OneObject: void m()>[0@L7] new A}]:<A: void set(B)>/%this -> [[]:NewObj{<OneObject: void m()>[0@L7] new A}]
[NewObj{<OneObject: void m()>[0@L7] new A}]:<A: void set(B)>/b -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}]
[NewObj{<OneObject: void m()>[0@L7] new A}]:<java.lang.Object: void <init>()>/%this -> [[]:NewObj{<OneObject: void m()>[0@L7] new A}]
[NewObj{<OneObject: void m()>[3@L8] new A}]:<A: void <init>()>/%this -> [[]:NewObj{<OneObject: void m()>[3@L8] new A}]
[NewObj{<OneObject: void m()>[3@L8] new A}]:<A: void doSet(B)>/%this -> [[]:NewObj{<OneObject: void m()>[3@L8] new A}]
[NewObj{<OneObject: void m()>[3@L8] new A}]:<A: void doSet(B)>/p -> [[]:NewObj{<OneObject: void m()>[9@L10] new B}]
[NewObj{<OneObject: void m()>[3@L8] new A}]:<A: void set(B)>/%this -> [[]:NewObj{<OneObject: void m()>[3@L8] new A}]
[NewObj{<OneObject: void m()>[3@L8] new A}]:<A: void set(B)>/b -> [[]:NewObj{<OneObject: void m()>[9@L10] new B}]
[NewObj{<OneObject: void m()>[3@L8] new A}]:<java.lang.Object: void <init>()>/%this -> [[]:NewObj{<OneObject: void m()>[3@L8] new A}]
[NewObj{<OneObject: void m()>[6@L9] new B}]:<B: void <init>()>/%this -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}]
[NewObj{<OneObject: void m()>[6@L9] new B}]:<java.lang.Object: void <init>()>/%this -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}]
[NewObj{<OneObject: void m()>[9@L10] new B}]:<B: void <init>()>/%this -> [[]:NewObj{<OneObject: void m()>[9@L10] new B}]
[NewObj{<OneObject: void m()>[9@L10] new B}]:<java.lang.Object: void <init>()>/%this -> [[]:NewObj{<OneObject: void m()>[9@L10] new B}]
[]:<OneObject: void m()>/a1 -> [[]:NewObj{<OneObject: void m()>[0@L7] new A}]
[]:<OneObject: void m()>/a2 -> [[]:NewObj{<OneObject: void m()>[3@L8] new A}]
[]:<OneObject: void m()>/b1 -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}]
[]:<OneObject: void m()>/b2 -> [[]:NewObj{<OneObject: void m()>[9@L10] new B}]
[]:<OneObject: void m()>/temp$0 -> [[]:NewObj{<OneObject: void m()>[0@L7] new A}]
[]:<OneObject: void m()>/temp$1 -> [[]:NewObj{<OneObject: void m()>[3@L8] new A}]
[]:<OneObject: void m()>/temp$2 -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}]
[]:<OneObject: void m()>/temp$3 -> [[]:NewObj{<OneObject: void m()>[9@L10] new B}]
[]:<OneObject: void m()>/temp$4 -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}]
[]:<OneObject: void m()>/x -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}]
Points-to sets of all static fields
Points-to sets of all instance fields
[]:NewObj{<OneObject: void m()>[0@L7] new A}.f -> [[]:NewObj{<OneObject: void m()>[6@L9] new B}]
[]:NewObj{<OneObject: void m()>[3@L8] new A}.f -> [[]:NewObj{<OneObject: void m()>[9@L10] new B}]
Points-to sets of all array indexes
#reachable methods: 8
----------Reachable methods:----------
<A: B get()>
<A: void <init>()>
<A: void doSet(B)>
<A: void set(B)>
<B: void <init>()>
<OneObject: void m()>
<OneObject: void main(java.lang.String[])>
<java.lang.Object: void <init>()>
#call graph edges: 11
----------Call graph edges:----------
<A: void <init>()>[0@L17] invokespecial %this.<java.lang.Object: void <init>()>(); -> [<java.lang.Object: void <init>()>]
<A: void set(B)>[0@L21] invokevirtual %this.<A: void doSet(B)>(b); -> [<A: void doSet(B)>]
<B: void <init>()>[0@L33] invokespecial %this.<java.lang.Object: void <init>()>(); -> [<java.lang.Object: void <init>()>]
<OneObject: void m()>[1@L7] invokespecial temp$0.<A: void <init>()>(); -> [<A: void <init>()>]
<OneObject: void m()>[4@L8] invokespecial temp$1.<A: void <init>()>(); -> [<A: void <init>()>]
<OneObject: void m()>[7@L9] invokespecial temp$2.<B: void <init>()>(); -> [<B: void <init>()>]
<OneObject: void m()>[10@L10] invokespecial temp$3.<B: void <init>()>(); -> [<B: void <init>()>]
<OneObject: void m()>[12@L11] invokevirtual a1.<A: void set(B)>(b1); -> [<A: void set(B)>]
<OneObject: void m()>[13@L12] invokevirtual a2.<A: void set(B)>(b2); -> [<A: void set(B)>]
<OneObject: void m()>[14@L13] temp$4 = invokevirtual a1.<A: B get()>(); -> [<A: B get()>]
<OneObject: void main(java.lang.String[])>[0@L3] invokestatic <OneObject: void m()>(); -> [<OneObject: void m()>]
----------------------------------------

其中每个变量和对象前的 [...] 代表了其上下文。

此外,Tai-e 将被分析程序的各个类的 IR 输出到 output/ 文件夹中,这些 IR 被存储为 .tir 文件,它们能够被通用的文本编辑器打开。

我们为本次作业提供了测试驱动类 pascal.taie.analysis.pta.CSPTATest,你可以用它来测试你的实现是否正确。

我们鼓励你使用不同的上下文敏感策略来分析本作业中的测试用例(例如 OneObject.java),并观察使用不同上下文敏感策略进行分析时,得到的指针集和调用图的精度差异。

4 通用准则

  • 在这次作业中,你只需要保证实现的正确性。不必过早考虑效率。

  • 禁止把你的作业包发给其他人参考。

  • 禁止抄袭。自己的工作必须由自己完成。

6 作业提交

你应当提交一个 zip 文件,其中包括你实现好的以下类:

  • Solver.java
  • _1CallSelector.java
  • _1ObjSelector.java
  • _1TypeSelector.java
  • _2CallSelector.java
  • _2ObjSelector.java
  • _2TypeSelector.java

7 评分

你可以参照 实验作业评测系统使用指南 来使用我们的作业评测系统对你完成的作业进行评分。

你的分数将取决于你实现的正确性:我们会用你提交的实现来分析 src/test/resources/ 目录下的测试用例和另外一些未公开的测试用例,然后将得到的结果与标准实现的分析结果进行比较,从而进行评分。

祝君好运!

Last Updated: