Skip to content

算法Algorithm

对象敏感指针分析

Object-sensitive pointer analysis · Object sensitivity

为什么用接收者的分配身份区分方法实例,比仅记调用位置更适合某些面向对象程序。

形式陈述 ​

对象敏感指针分析以接收者对象的抽象身份区分实例方法的不同分析上下文。它通常在包含式points-to约束上增加方法上下文和堆上下文,再与调用图共同迭代。

固定无反射、无动态装载的Java式核心语言,字段通过对象引用访问。基础1-object-sensitive版本用接收者的分配站点h作为方法上下文。若在调用者上下文 κ 中分析 y=x.m(a),对每个 h∈pt(x,κ):

  1. 按h的类求实际目标方法 mh
  2. 建立方法实例 (mh,h),把this绑定为h
  3. 把实参集合传到该实例的形参槽
  4. 把该实例的返回集合传回当前调用的结果槽y

字段槽按抽象对象和字段名表示,例如 pt(h,f)。this.f因此在不同接收者上下文下读取不同槽;仅把局部变量分上下文,却把全部对象的同名字段合成一格,会失去大部分收益。

更一般的k对象敏感和堆敏感方案,需要明确选取哪段接收者/分配上下文,并保持有限。这里的对象身份不是运行时地址;一个分配站点可能代表无穷多真实对象。

直觉

面向对象代码常有大量薄包装方法:容器的read调用get,get再访问this的字段。不同对象可能沿同一段方法代码、同一个内部调用点流动,因此仅记最近调用点未必能区分它们;接收者身份则一路跟着对象走。

对象敏感性是上下文敏感性的一种选择,不是另加一个运行时检查。分析器分别保存同一方法作用在不同抽象对象上的结果,程序实际执行方式没有改变。

例子与边界

同一个内部调用点,两个对象上下文 ​

类Box含字段value,方法为:

text
Box.get():  return this.value
Box.read(): return this.get()    // 内部调用点c

主程序在两个不同分配站点创建Box:

text
b1 = new Box@h1(new Red@r)
b2 = new Box@h2(new Blue@b)
x = b1.read()
y = b2.read()

假设构造器仅将实参写入自己的value字段。字段敏感的堆事实为

pt(h1,value)={r},pt(h2,value)={b}.

上下文不敏感分析把read和get中的this都合成 {h1,h2},于是两个调用的结果都可能是r或b。1-object-sensitive分析则保存:

方法实例 this get/read返回
(read,h1) {h1} {r}
(get,h1) {h1} {r}
(read,h2) {h2} {b}
(get,h2) {h2} {b}

两次进入get都来自同一个源码调用点c,但接收者不同,因此没有合并。最终pt(x)只有r,pt(y)只有b。这个例子刻意把外层read也按接收者区分,否则一个已经合并的外层摘要仍可能把返回结果再混在一起。

方法上下文与堆上下文是两项选择 ​

若改由一个工厂方法在同一分配站点h创建两个Box,基础分配站点抽象会把它们都表示成h。即使实际运行地址不同,1-object-sensitive方法上下文仍只有h,不能凭空把它们分开。

可以让工厂分别在两个可区分的创建上下文 η1,η2 中运行,并把堆对象表示成 (h,η1) 与 (h,η2)。之后的方法上下文再按这些堆名选取。若只增加方法上下文却不细化分配结果,两个对象在堆里已经合并,后续this分析无法恢复丢掉的身份。

对象敏感不普遍胜过调用点敏感 ​

同一个服务对象处理两个不同输入时,接收者身份相同;对象敏感可能合并两次调用,而不同调用点可以区分。静态工具函数没有this,也要另选上下文策略。实验中某一方案更准,不能升级为对所有程序都严格支配其他方案的定理。

推论与应用

求解仍是有限包含约束的单调传播。新points-to事实可能发现新接收者,从而激活新方法实例;新实例又产生字段写入与返回事实,所以调用边和points-to不能只各算一遍就固定。

可靠性要求每个真实对象都映到一个允许的抽象堆名,每次真实实例调用都创建相应抽象方法上下文,参数、this与返回映射都覆盖实际值。上下文划分可以粗,但不能漏掉可能接收者;过度“挑选最像的一个对象”会破坏安全性。

若有H个抽象对象、m个实例方法,基础1对象敏感最多产生mH个方法实例;每个实例还带本地变量事实,字段槽按对象/字段分配。把这些展开后的数量代入包含求解器的成本即可得到界。不能仍以原始源码大小n直接套用无上下文分析的 O(n3),也不能忽略堆上下文数本身可能增长。

结果常使虚调用目标、修改副作用和对象间不别名结论更精确。是否值得增加上下文,应看误报实际来自接收者混合、工厂分配合并,还是完全不同的路径条件;错误地细化不相关维度只会增加成本。

练习与解答 ​

把主例的两个Box都改为同一站点h分配,但仍让一个装Red、一个装Blue。在不增加堆上下文时,字段槽 pt(h,value) 为 {r,b},read/get的唯一对象上下文也是h,故x、y都得到 {r,b}。

验收要先指出合并发生在堆对象命名阶段,再说明其如何传到this与字段读取;只说“把k加大”而不规定堆名怎样变化,尚未给出可执行改进。

参考资料
关系图谱10 个相邻概念 · 2 类关系

拖动节点调整位置。

显示关系

显示:依赖

  1. 前置三跳
  2. 前置二跳
  3. 前置一跳
  4. 当前条目
  5. 后续一跳
  6. 后续二跳
  7. 后续三跳
文字版关系按与当前条目的最短距离分组
类型化关系

使用的工具