"可能未初始化变量"分析的目标是在给定程序的每个程序点上近似地确定哪些变量可能具有来自未初始化变量的值。这个分析非常类似于污点分析,后者旨在推断哪些计算可能涉及来自不受信任的输入的"污点"数据。
以下是一个示例程序,其中以注释的形式显示了每个程序点的"可能未初始化的变量":
var a,b,c; // a,b,c
a = input; // b,c
b = a + c; // b,c
if (input) { // b,c
c = 1; // b
} // b,c
在变量声明之后的程序点上,所有三个变量都可能未初始化。请注意,在赋值 b = a + c 之后,b 仍然可能未初始化,因为其值取决于 c(因此,这个分析并不是 Section 5.9 中初始化变量分析的对偶)。在最终的程序点上,尽管执行了 c = 1 的赋值,但 c 仍然可能未初始化,因为包含该赋值的分支可能不会被执行。
我们将设计这样一种分析,它既是流敏感的(遵循第5章);又是上下文敏感的(第八章),使用上下文敏感的函数方法。如第8.4节所讨论的,上下文敏感的函数方法足够精确,可以完全避免跨过程的无效路径问题。这种分析的结果可以用于以下方式。在分析程序时,我们想知道对于给定的变量X和函数f内的程序点v,是否可能以某种上下文c调用f,以便在上下文c中v处的X可能未初始化 - 如果是这样,可以发出警告,通知程序员如果v处的指令使用X,则程序行为可能是程序员所未知的情况,也就是错误的情况。(也就是说,在明确的函数调用关系、明确的路径下会有问题)
首先,我们需要选择一个合适的格来描述抽象状态
$$ States=\mathcal{P}(Vars) $$
直观地说,每个抽象状态表示一组可能未初始化的变量。在函数式方法中,上下文敏感性的上下文本身就是抽象状态,因此我们设置上下文(Contexts)等于状态(States),这样具有n个控制流图节点的程序的分析格看起来如下所示:
$$ (~\mathcal{P}(Vars) \rightarrow lift(\mathcal{P}(Vars)) ~)^n $$
对于CFG节点v,这个格的元素是一个函数$m_v: \mathcal{P}(Vars) \rightarrow (\mathcal{P}(Vars) \cup \{\text{unreachable}\}$,是从变量集合到变量结合加上不可达特殊元素的函数,叫做跳转函数。这样的函数有一个很直白的含义,对于一个函数的入口点v,如果调用这个函数的时候存在可能未初始化的变量集合是s(也就是$s \in \mathcal{P}(Vars)$),那么在v这个点可能未初始化的变量集合就是$m_v(s) \in \mathcal{P}(Vars)$ 或者 {unreachable}(表示没有这样的调用点,也就是说在上下文s里面,f函数和v不可达)。举个例子,如下函数foo,一个可能的调用上下文是y没有初始化,x一定初始化(也就是P(Vars)是 {y}),那么在经过foo的return语句之后,计算的结果$m_{\text{return z;}} ~(\{y\}) = \{y, z\}$ 。
foo(x, y) { // x ok ,y 没有初始化
var z; // z没有
z = x - y; // z 没有,因为有y
z = z * z; // z 没有,因为z
return z; // y和z
}
主函数入口永远可达,并且没有上下文,因此约束规则:
对于变量声明,var X,TIP语言规定声明的变量没有初始化,因此转换方程定义:
对于赋值语句,X = E,令vars(E)表示X中出现的变量的集合,因此转换方程定义:(来源s里面,只要有未初始化,那么X就是未初始化)
如8.2节里面,对于CFG中变量声明或者赋值的节点v,分析约束[[v]]可以通过转换方程和JOIN函数来表示
最后,我们可以在每个程序节点收集可能未初始化变量的集合: