在阅读代码时,准确适当的注释能够给开发者提供很有价值的帮助。但并不是每一个需要阅读的代码都包含注释,这可能会让开发者白白花费更多用于理解代码的时间。特拉华大学软件分析实验室的一项研究试图通过数据驱动的技术来解决这一问题,为与对象相关的语句序列自动生成自然语言描述,从而帮助开发者更有效率地理解阅读代码。该论文也是 SANER 17(IEEE 软件分析演进与逆向工程国际大会) 会议接收的论文之一。注:为了区分代码 method 和本论文中提出或提到的解决方案,本译文中将 method 译为「方法」,将 approach 译为「办法」。
软件维护工作中的很大一部分时间花费是理解已有的代码。通过帮助开发者快速理解代码和减少他们的阅读代码量,可以减少开发者理解程序的时间。当程序员书写注释时,这些注释的目的是描述该方法(method)。不幸的是,程序员常常不写注释,而且有时候注释是过期的。使用空行隔开不同方法的代码可以帮助阅读者理解代码;但是,他们仍然需要阅读代码。另一种办法是 method refactoring,以使用方法调用(method call)来替代它们,这样也能压缩阅读量;但是,这种转换需要繁琐的分析才能维持原有的语义。
为了解决程序员写注释的问题,以帮助提升对程序的理解,之前 Sridhara(特拉华大学软件分析实验室)通过人工的方式为少量已知的多语句动作编写了一套模板,比如用于「计算最大值」的循环结构。这样所得到的注释可以方法(method)的注释。为了避免手动开发模板,我们提出了一种机器学习的方法自动识别 Java 方法中的循环动作。我们的方法不需要手动构建模板,自动的特征提取方法可以应用到大数据中。
我们在之前的研究成果中将动作单元(action unit)的概念定义为:逻辑上实现了一个高层面动作(即一个高层面算法步骤)的由连续语句序列构成的代码块。一个方法通常包含多个动作单元,其中动作单元是很小的而不足以构成单个方法,但却需要多个语句来实现。尽管之前的研究都使用了结构信息和语言信息来识别用于实现高层面算法步骤(动作单元),但是这他们都只解决了程序中的循环语句。
还有一种未被考虑过的,同时也是非常重要的动作单元,是与对象相关的动作单元。我们将与对象相关的动作单元定义为仅由非结构化连续语句构成的动作单元且这些非结构化连续语句是通过对象互相关联在一起的。非结构化语句是变量声明/赋值或方法调用语句。比如,在 Listing 1 中,第 11-14 行表示了一个与对象相关的动作单元,这是通过 mappingRow 对象关联的。与对象相关的动作单元通常是实现一个方法的中间步骤,而且在代码中非常常见。在从 GitHub 随机选择的 1000 个开源项目中,我们发现用空行隔开的代码块中有 23.03% 都是与对象相关的动作单元。
Listing 1:阐释动作单元
除了识别实现高层面动作的代码,我们还想通过简短的英语短语来描述它。对于 Listing 1,我们之前的技术 [3] 会将第 2-7 行的第一个动作单元识别为 if 循环并将其描述为「确定一个元素是否存在于该比特流中」。对于第 11-14 行的动作单元,这篇关于与对象相关的动作单元的论文会生成「将新创建的映射行添加到数据库中」。对于第 9 行单行的与对象相关的动作单元,我们会生成「将指定比特流添加到比特流」。
与对象相关的动作单元的概念催生出了一种用于生成方法总结注释的新方式,尤其是对于较长的方法。Sridhara et al. 提出的之前最佳方法是根据它们的特点选择单独的语句,然后为这些单独的语句生成用于方法总结的英语短语。对于 Listing 1,用于内容选择的 Sridhara 方法会选择第 9、12、13、14 行,其中第 12 和 13 行之后会被过滤为普遍动作。第 9 行被选择的原因是这是一个 void return 语句,第 14 行被选择的原因是这是一个 void return 和结束语句。尽管最终选择的第 9 和 14 行内容能够得到一个合理的方法总结,但应当指出选择它们的原因并不是为了识别高层面动作。因此,在 Listing 2 中,Sridhara 的方法会选择第 4-6、8 和 10 行。第 4-6 行会被看作是一个高层面动作序列(因为它们都是一样的动作)。
Listing 2:用于总结注释生成的动作单元
通过本论文提出的方法识别与对象相关的动作单元,我们可以将 Listing 2 中的第 2-8 行识别为一个与对象相关的动作单元,第 10 行识别为另一个与对象相关的动作单元。因为我们能识别出第 8 行终止了一个完整的与对象相关的动作单元,我们为该动作单元生成「为新创建的 Json 解析器解析坐标」,这从第 8 行和第 3 行中提取了内容。更重要的是,第 4-6 行没有被识别为一个需要通过总结描述的单独的动作单元。由于这种差异,Sridhara et al. 会在总结中包含「获取下一个 token」,而这并不适合用在该总结语句中。
除了用于方法总结注释生成,自动识别和描述动作单元还有其它应用。我们生成的描述可以用作任何被识别出的动作单元的内部注释。被识别出的动作单元可以用作用于提取方法重构的源,并且生成的描述还可以在调整之后用于为重构代码生成方法名。在生成 API 用途的代码范例上存在一些研究努力,比如 [5];而我们观察到这些代码范例中很多都是动作单元。因为与对象相关的动作单元出现得很频繁,所以本研究成果可用于为识别出的范例提供描述。另一个潜在的应用是帮助没有头绪的程序员快速理解代码段,否则这件事做起来就会很繁琐和困难。
本论文的主要贡献包括:
一个用于识别实现了单个高层面动作的方法内的与对象相关的动作单元的算法
用于合成简洁且准确的表达与对象相关的动作单元的高层面动作的自然语言描述的规则
在为与对象相关的动作单元自动生成自然语言描述上进行了评估研究,表明人们对我们的方法的有效性有很强的正面的整体看法
我们的自动系统仅涉及源代码分析,不需要任何执行信息,因此可以用于不完整的和不可执行的遗留系统。我们的系统可以轻松地集成到 IDE 中,从而可在软件开发者开始开发 Java 方法时提供最新的描述。
问题描述
我们在这篇论文中解决的问题是:给定一个 Java 方法 M,自动发现每个与对象相关的动作单元,这些与对象相关的动作单元实现了组成 M 的整体算法的高层面动作;然后用简洁的自然语言描述准确地表达每个动作单元。
方法概述
图 1 描绘了为与对象相关的动作单元进行自动识别和自然语言描述生成的主要步骤。给定一个 Java 方法作为输入,我们构建一个抽象句法树(AST:abstract syntax tree),并同时使用 AST 和在相关对象上执行的操作来识别方法中的与对象相关的动作单元。对于每个与对象相关的动作单元,我们要识别出表示该动作单元的主要动作的语句,我们将其称为该动作单元的焦点语句(focal statement)。焦点语句能为该动作单元的自然语言描述提供主要内容。我们会识别焦点语句中应该被包含进生成的描述中的动作和参数,然后用词汇描述这些动作和参数以创建一个自然语言短语。
图 1:为与对象相关的动作单元生成描述的整体过程
焦点语句、动作和参数的识别以及词汇化都是通过我们开发的一组规则执行的,这些规则来自对大量 Java 方法的一项数据驱动的研究。我们的大部分工作都是开发这些规则。我们通过人工的方式分析了大量从开源项目中随机选择的语句序列,并开发了与对象相关的动作单元的模板。在我们分析中,我们使用了空行来为我们提供学习这些模板的样本,因为 Java 开发者传统上会使用空行来将方法分割成逻辑上相关的各个部分 [7,8,9]。因此,在学习开发者如何将一个方法分成多个算法步骤方面,空行是一个重要的来源。尽管我们使用了空行来学习用于识别与对象相关的动作单元的模板,但我们的基于模板的识别方法并不依赖于方法中的空行。如果空行存在,该算法会使用空行;但就算没有空行,它也能识别与对象相关的动作单元。
动作单元和焦点语句识别
如图 2 所示,一个动作单元通常包含 3 个部分。(1)是对对象引用 o 的声明或赋值。(2)是一个或多个语句,其中每个语句都是在对象 o 上的一次方法调用。(3)是使用对象 o 的一个语句。具体来说,本研究中的「使用」是指 o 在有声明/赋值的情况下出现在「=」的右侧。如果没有声明/赋值,那么 o 将作为一个方法调用的参数出现,并且该方法调用不是在 o 上调用的。这 3 个部分中每个部分都是可选的。
图 2:与对象相关的动作单元的一般格式
对于焦点语句的位置,我们分析得到了 3 种主要情况。注意这里无需执行数据流分析,因为我们只需要确定对象名的复用。
情况 1:(3)部分存在
情况 2:(3)部分不存在
情况 3:多个对象
识别动作和参数
Listing 11 中的动作单元及其焦点语句给出了一个完整的示例;根据识别动作和参数的规则,动作是「set」,主题是「information」,辅助参数为「config」。
Listing 11:示例代码段
动作和参数的词汇化
我们可以根据焦点语句识别动作和参数。接下来,我们生成包含与之相关的动作和参数为内容的自然语言描述。我们利用了之前基于源代码得到词汇的研究成果 [13,2]。有了动作和参数之后,我们按以下模板生成一个动词短语:动作 主题(辅助参数)
评估
我们的评估设计的目的是回答这三个研究问题:
研究问题 1:动作和参数选择的效果如何?
研究问题 2:文本生成的效果如何?
研究问题 3:描述生成器会消耗多少时间?
表 1:人类对描述的评价的分布
表 2:对系统生成的描述和人类书写的描述的看法的混淆矩阵
论文:为与对象相关的语句序列自动生成自然语言描述(Automatically Generating Natural Language Descriptions for Object-Related Statement Sequences)
论文地址:https://www.eecis.udel.edu/~xiwang/saner17.pdf
当前驱动软件维护工具的源代码分析要么将方法(method)当作是单个单元,要么就将其当作是单独的语句或词构成的集合。它们往往会利用方法名(method name)或任何已有的内部注释。但是,内部注释很少见,而方法名通常又不能体现该方法的多个高层面的算法步骤——这些步骤太小了以至于不能成为一个单独的方法,但却需要多个语句才能实现。之前的研究已经证明了自动确定循环(loop)的高层面动作(action)的可行性;但是,很多高层面的动作都仍未得到解决和记录,尤其是主要通过对象引用互相关联的连续语句序列。我们将其称为与对象相关的动作单元(object-related action unit)。
我们在这篇论文中提出了一种办法,可用于自动生成方法内的与对象相关的动作单元的自然语言描述。我们的办法是利用大量可用的高质量开源项目来学习与对象相关的动作的模板,确定可以代表主要动作的语句,然后生成这些动作的自然语言描述。我们在 100 个与对象相关的语句序列构成的集合上进行了评估研究,结果表明我们的办法有希望自动识别动作和参数以及生成自然语言描述。