量化Javascript中可检测的BUG

paper

这篇文章探讨了是否值得为Javascript项目添加静态类型注释,以及Facebook的Flow和Microsoft的Typescript的比较。这些静态系统是否真的能够检测到BUG?

静态动态之争

开发语言中有动态类型和静态类型语言一分,动态类型也称弱类型语言,如Javascript,Python等。动态类型语言对类型要求不严格,在运行时做数据类型检查,这样可以写出干净(clean)的代码、开发灵活、可扩展,但是有些代码BUG不能及时发现。相反,静态类型语言也称为强类型语言,如C、C++、Java等。静态类型语言对数据类型要求严格,在编译时做数据类型检查,这样可以及时在编译时发现代码BUG,优化编译器等。

Javascript是动态类型语言,而Flow和Typescript分别是Facebook和Microsoft针对Javascript推出的静态系统,是Javascript的超集,即将Javascript在一定程度上变成静态类型语言。

作者研究了GitHub中现实世界中JavaScript项目中的历史bug。如果开发者当时一直在利用Typescript或Flow,那么这个BUG会不会通过类型检查器呢?如果不通过的话就可以合理地假设这个BUG从一开始就不会被开发人员提交到库(repo)中。

下面是一个类型注释可以检测出来的BUG:

类型注释可以检测出来的BUG
类型注释可以检测出来的BUG

BUG评估

为了弄清楚Typescript和Flow可以检测到多少这样的bug,需要一些规则来说明添加类型注释是否可行,并且这些BUG需要人工去注释,添加类型注释需要花费多长时间。一项针对78个bug的小样本的初步研究表明,在绝大多数时候(90%),可以在10分钟内得出结论,所以作者设定允许花在注释一个bug上的最长时间被设定为10分钟。

每个bug都用Typescript 2.0和Flow 0.30进行评估。为了减少类型系统对学习效果的影响,随机选择要先尝试的类型系统。然后,读取BUG的错误报告和BUG修复情况,并用10分钟的时间人工添加类型注释。有时,即使不加注释工具都可以检测到BUG,但也有一些BUG的结果报告表明这个BUG错误与类型无关,在这种情况下,BUG将被标记为无法检测。

实验结果

在本文评估的400个公共BUG中,Flow成功地检测到了其中的59个BUG,而Typescript检测到了其中的58个BUG。评估过程中遇到的主要障碍包括:复杂的模块依赖关系、某些模块缺少带注释的接口、程序的理解一般很困难。

本文将所有400个BUG标记为:可检测的和在Flow和TypeScript中无法检测到的。在总共60个BUG中,Flow检测了一个,Typescript检测到了另外两个。对结果进行测试表明,在95%置信水平,Flow和Typescript可检测的BUG真实百分比为11.5%、18.5%,平均为15%。

Typescript和Flow哪个更好?

这两个系统可以检测到的BUG大部分都是重叠的,只有3个BUG只有Typescript才能检测到,3个BUG只有Flow才能检测到。

image-20180722004944862
image-20180722004944862

Typescript未能检测的所有三BUG都String类型的null值,或undefined有关。Flow无法检测到的三个BUG中的两个是由于流不完全支持在索引中使用string

结论

本文作者评估了静态类型系统为Javascript代码提供的BUG检测优势。结果表明,使用Flow或Typescript可以有效减少Github上公开项目的15%的BUG。此工作是第一个在公开成熟的代码上对Javascript的静态类型系统进行性能评估的。通过本文的研究,从业者可以通过对Flow和Typescript能够检测到的BUG进行分类,发现两者之间的差异,并决定是否在实践项目中为Javascript采用此静态类型系统。

静态检测Javascript中Web API请求

介绍

本文提出了一种用Javascript静态检查Web API请求的方法:首先将请求中的URL、HTTP请求方法、请求数据提取出来,然后检查是否符合Web API规范(如API提供者编写的Swagger文档)。因为Web API规范要求:Javascript程序对Web API执行的HTTP请求,请求内容需包括:请求地址URL,HTTP请求方法,和正确的请求数据。但是Javascript的编译时无法检查构造的请求是否符合Web API的要求。本文通过从Github收集Javascript文件中的Web API请求进行实验验证。从收集到的6575个请求中,本文使用的方法确定请求的URL和HTTP方法是否与Web API规范是否一致,实验结果表明,方法的识别精确度为96.0%。对实验结果进行分析发现,其中很多都是由于在请求客户端的代码中写了错误的代码造成的。

应用程序通过使用其支持的HTTP方法之一(包括GET、POST、PUT、DELETE等)向服务端URL发送HTTP请求来调用Web API;而所需的请求数据作为查询或路径参数发送,或在HTTP请求体(request body)中发送。需要发送的URL、HTTP方法和请求数据基本上都是字符串,由应用程序构造。当请求的目标是不存在的URL或发送不符合Web API需求的数据时,会发生运行时错误。但是在客户端编写代码时并不会有类型安全检查,即客户端必须单独进行运行时测试,才能知道编写的HTTP请求代码是否正确。

检测过程

检测过程的目的是将静态分析产生的信息与相应的Web API规范相匹配,从而发现客户端HTTP请求的实现和规范之间的不一致。通常,请求信息包括:(1)要调用的Web API的URL,包括endpoint和路径,和一个可选的查询参数字符串;(2)HTTP方法,包括GET、POST、PUT、DELETE等;(3)在请求的有效负载(payload)中发送的请求数据。实际中,本实验会采用多个URL值、HTTP方法和请求数据来检索单个请求,只要有任意一个这些数据的组合符合Web API规范即认为没有问题,不会报告错误,以此来避免部分false positive的错误。

1. 检查endpoint

该过程主要是将请求的endpoint和API规范中定义的endpoint相比较。首先检查给定请求的任何URL是否以已知的API规范的任何协议开头:比如,HTTP或HTTPS。然后,该过程尝试将请求的endpoint中的路径和规范中定义的路径相比较。为了匹配路径,该过程获取请求的每个UR,并将其与之前与该请求匹配的每个路径定义相比较。然后,通过检查每个路径的部分(由"/"分隔),将剩下的路径字符串与规范中的路径定义进行比较。最后,该过程确定HTTP方法是否符合规范,如HTTP请求的header是否符合Web API开发者制定的API规范。

2. 检查请求数据

对于可以匹配API规范中的endpoint的请求,该过程会检查请求数据(如果有)的有效性。请求数据有两种:1. 在请求体中发送的数据(HTTP方法通常是:POST、PUT、或PATCH);2. 在查询字符串中发送的数据。

(1). 检查有效负载数据:在HTTP请求的有效负载中发送的数据可以是任何格式。由于静态分析关注的是Javascript,并且因为JavaScript对象表示法(JSON)也是Web API中流行的数据格式,因此本文关注的是JSON中的数据。Swagger规范中允许定义有效负载数据,关于特定路径的或是针对特定endpoint的都可以。如果有已匹配到的规范在匹配到的endpoint中定义了有效负载的格式,那么这个过程将确定请求信息中报告的有效负载是否遵循这个格式。

(2). 检查查询参数:查询数据被编码为key-value键值对。在API规范中,查询参数可以定义为可选的。然后,检查过程可以确定请求中是否存在所有必需的查询参数。同样,该过程考虑了规范中不同位置的查询参数的定义。该过程将查询参数解析为请求报告的所有URL的查询字符串。然后,该过程检查找到的任何参数集是否与匹配请求的任何端点定义中找到的参数定义相匹配。

结论

本文利用基于框架的Javascript Web应用程序的静态分析的现有研究,创建了一个能够提取与Web API请求相关的字符串的分析工具。本文作者利用这些提取的请求数据作为检查器的输入,而检查器来确定请求是否和Web API开发者制定的API规范是否一致。本文对6575个请求的检查结果进行了定性分析,结果显示,大多数不一致都是由于客户端代码中的错误,比如,调用已废弃的API、URL中的错误、数据有效负载定义中的错误和不完整的Swagger规范。这些结果表明,此工具能够警告程序员源代码中包含不一致的Web API请求。因此该工具可以与支持开发人员使用Web API的现有工具进行集成。

开发者如何修复跨项目相关的BUG?

介绍

GitHub是目前开源世界中最大的代码存储库,它已经培育出了各种软件生态系统,在这些系统中,项目相互依赖。这个生态系统中的项目通常具有复杂的相互依赖关系,这种依赖关系给BUG查找和修复带来了新的挑战。本文对交叉项目的相关BUG进行了实证研究,将相关的BUG报告给不同的项目,本文主要关注两个方面:1. 开发者如何跟踪导致项目BUG的根本原因?2. 下游开发人员如何协调处理上游BUG。本研究揭示了开发人员常见的实践方式以及修复跨项目BUG的各种因素。这些发现为未来系统范围内的软件BUG分析提供了提示,同时也阐明了issue追踪器对此类BUG的需求。

Github为每个存储库(repo)提供了一个问题(issue)追踪系统,用于报告和讨论关于BUG的问题,因此多个关联项目BUG之间的关系非常可能存在于这些issue追踪系统之中。

比如:Astropy项目依赖于Numpy项目,而Astropy项目发现了一个性能问题(issue id #4259),开发人员检查问题后发现,这个问题可能源于它的上游项目Numpy。然后另一个对Numpy和Asstropy项目都有贡献的开发人员指出,Numpy问题(issue id#6467)可能是与Astropy 4259问题是相关的。这就是所谓的跨项目的BUG。

修复BUG的实践已经研究多年,然而很少有工作研究开发人员如何修复跨项目的BUG,特别是如下两个挑战:1. 跨项目BUG的根本原因的跟踪:当BUG从一个项目转移到另一个项目时,将BUG追溯到其根源变得更加困难;2. BUG修复的协调工作:在等待上游修复时,下游的开发人员需要协调他们的项目与上游项目,以尽量减少上游BUG对项目的任何不良影响。

这两个挑战并没有在修复项目内的BUG中遇到,它们导致了跨项目BUG跟踪和修复的高度复杂性。由于缺乏这些方面的经验证据,本研究旨在研究开发人员如何处理跨项目相关的BUG,特别是在面对这两个挑战时。本文的研究基于科学Python生态系统,这是GitHub上最著名的生态系统之一。结合手工检查271对错误和在线调查,主要有两个发现:首先,堆栈跟踪、与上游开发人员的沟通以及对涉及项目的熟悉程度是有助于跨项目根源跟踪的三个主要因素。其次,在下游开发人员等待上游修复BUG时,可以提出一个无版本依赖的解决方案。

结论

本文研究了开发人员如何修复交叉项目相关的BUG,例如。在GitHub的生态系统中,在不同的项目中,有一对因果相关的BUG。本文主要关注两个方面:1. 跨项目根源跟踪;2. BUG修复过程中上下游项目之间的协调。本文作者手动识别和检查了scientific Python生态系统中的271对跨项目BUG。实验结果揭示了开发人员在修复跨项目BUG时的常见做法。