引用:Arianna Blasi, Alberto Goffi, Konstantin Kuznetsov, Alessandra Gorla, Michael D. Ernst, Mauro Pezzè, and Sergio Delgado Castellanos. 2018. Translating Code Comments to Procedure Specifications. In Proceedings of 27th ACM SIG- SOFT International Symposium on Software Testing and Analysis (ISSTA’18). ACM, New York, NY, USA, 12 pages. https://doi.org/10.1145/3213846.3213872.
摘要
過(guò)程規(guī)范在許多軟件開發(fā)任務(wù)中很有用。例如,在自動(dòng)生成測(cè)試用例的過(guò)程中,它們可以指導(dǎo)測(cè)試,充當(dāng)能夠發(fā)現(xiàn)錯(cuò)誤并識(shí)別非法輸入的測(cè)試先知。盡管實(shí)際上很少有正式的規(guī)范,但是對(duì)于開發(fā)人員來(lái)說(shuō),使用半結(jié)構(gòu)化注釋來(lái)記錄其代碼是標(biāo)準(zhǔn)做法。這些注釋通過(guò)預(yù)定義標(biāo)簽和自然語(yǔ)言的組合來(lái)表達(dá)過(guò)程規(guī)范。本文介紹了 Jdoctor,該方法結(jié)合了模式,詞法和語(yǔ)義匹配,將 JAVAdoc 注釋轉(zhuǎn)換為以 Java 表達(dá)式編寫的可執(zhí)行過(guò)程規(guī)范。在實(shí)證評(píng)估中,將 Javadoc 轉(zhuǎn)換為過(guò)程規(guī)范時(shí),Jdoctor 的精度達(dá)到 92%,召回率達(dá)到 83%。
我們還將 Jdoctor 衍生的規(guī)范提供給自動(dòng)測(cè)試用例生成工具 Randoop。規(guī)范使 Randoop 能夠生成測(cè)試案例,從而減少誤報(bào)并揭示更多缺陷。
1 簡(jiǎn)介
程序規(guī)范表達(dá)了預(yù)期的程序行為,因此可以啟用或自動(dòng)化許多軟件工程任務(wù)。在軟件測(cè)試中,它用作確定哪些輸入合法和哪些輸出正確的預(yù)言 [4, 17, 31, 37]。在調(diào)試中,它標(biāo)識(shí)錯(cuò)誤的語(yǔ)句 [29, 54]。在代碼合成中,這是合成器致力于的目標(biāo) [28, 47]。在重構(gòu)中,它確保轉(zhuǎn)換是一致的 [23]。在形式驗(yàn)證中,它區(qū)分正確和錯(cuò)誤的實(shí)現(xiàn) [53]。在運(yùn)行時(shí)監(jiān)視中,它可以識(shí)別異常行為 [15]。自動(dòng)化(部分)任務(wù)需要工具可以操縱的機(jī)器可讀格式。正式的規(guī)范很好地達(dá)到了這個(gè)目的。但是,很少有正式的規(guī)范,因?yàn)榫帉懸?guī)范不是常見(jiàn)的軟件開發(fā)實(shí)踐。
? 相比之下,非正式規(guī)范很容易以自然語(yǔ)言編寫的半結(jié)構(gòu)化和非結(jié)構(gòu)化文檔的形式提供。程序員在過(guò)程文檔中指定前提條件、后置條件和特殊行為是標(biāo)準(zhǔn)做法。 Javadoc 標(biāo)記語(yǔ)言和工具于 1995 年在 Java 的第一個(gè)版本中出現(xiàn),而類似但跨語(yǔ)言的工具 Doxygen 在 2 年后出現(xiàn)了。 Java IDE 會(huì)自動(dòng)插入 Javadoc 注釋的模板。最重要的是,程序員已經(jīng)習(xí)慣于編寫這些注釋。結(jié)果,大量代碼包含了非正式的 Javadoc 規(guī)范。但是,軟件工程工具很少使用這些非正式規(guī)范。
? 本文介紹了 Jdoctor,它是一種從程序員已經(jīng)創(chuàng)建的工件(即 Javadoc 代碼注釋)自動(dòng)構(gòu)建可執(zhí)行過(guò)程規(guī)范的技術(shù),而無(wú)需程序員更改開發(fā)實(shí)踐或做額外的工作。可執(zhí)行規(guī)范是可以執(zhí)行的規(guī)范,例如,因?yàn)樗怯镁幊陶Z(yǔ)言而不是其他某種邏輯編寫的。它也需要在過(guò)程上表達(dá),而不是(例如)聲明性地要求某些值的存在而不指示如何計(jì)算它。由 Jdoctor 生成的過(guò)程規(guī)范可用于多種軟件工程任務(wù)中,例如自動(dòng)生成測(cè)試用例,如本文所示。
1.1 應(yīng)用:測(cè)試用例生成
自動(dòng)生成測(cè)試用例可以降低開發(fā)成本和軟件故障的社會(huì)成本 [5, 27, 44]。要自動(dòng)生成測(cè)試用例,測(cè)試生成器必須生成一個(gè)輸入,該輸入使被測(cè)程序執(zhí)行某些操作,并生成一個(gè) oracle 以確定程序是否正常運(yùn)行。
? 創(chuàng)建準(zhǔn)確的測(cè)試 Oracle 仍然是一個(gè)未解決的問(wèn)題。正如我們現(xiàn)在所解釋的,這將導(dǎo)致測(cè)試生成器同時(shí)遭受錯(cuò)誤警報(bào)(當(dāng)被測(cè)程序正確時(shí)測(cè)試失敗)和錯(cuò)過(guò)警報(bào)(當(dāng)被測(cè)程序有錯(cuò)誤時(shí)測(cè)試通過(guò))。例如,假設(shè)自動(dòng)生成的測(cè)試中的方法調(diào)用引發(fā)了異常。拋出此異常并不一定意味著所測(cè)試的方法有錯(cuò)誤。存在以下可能性:
(1)引發(fā)的異常實(shí)際上揭示了實(shí)現(xiàn)缺陷。一個(gè)示例是在主題程序中觸發(fā)斷言,或者以其他方式無(wú)法完成所請(qǐng)求的操作。
(2)拋出異常是預(yù)期的、期望的行為。一個(gè)示例是在不可變對(duì)象上調(diào)用變量時(shí)發(fā)生的IllegalOperationException。
(3)引發(fā)的異常是允許的,但不是必需的行為。例如,如果二進(jìn)制搜索例程的參數(shù)數(shù)組未排序,則其行為不確定。允許引發(fā)任何異常或返回與搜索到的元素是否在數(shù)組中不一致的結(jié)果。
在沒(méi)有規(guī)范的情況下,測(cè)試生成工具可以使用試探法來(lái)猜測(cè)給定行為是正確還是不正確。一種試探法是,如果某個(gè)方法的參數(shù)之一為null,則以NullPointerException結(jié)尾的方法執(zhí)行被認(rèn)為是正確的,而不管是否可能將其視為可接受行為的特定要求 [40]。另一試探法是使用回歸預(yù)言,它認(rèn)為正確的行為是受測(cè)試軟件的早期版本所暴露,并且認(rèn)為任何其他行為是錯(cuò)誤的,即使其他行為也為軟件設(shè)計(jì)人員所接受 [19]。
? 這些猜測(cè)還會(huì)導(dǎo)致誤報(bào)或警報(bào)遺漏,其中該工具無(wú)法報(bào)告暴露出錯(cuò)誤行為的測(cè)試。當(dāng)調(diào)用應(yīng)該引發(fā)異常但失敗時(shí),或者調(diào)用引發(fā)異常但工具試探性地忽略該異常以避免錯(cuò)誤警報(bào)時(shí),可能會(huì)發(fā)生這種情況。
? 我們建議測(cè)試生成器應(yīng)該利用通過(guò) Jdoctor 自動(dòng)生成的可執(zhí)行過(guò)程規(guī)范來(lái)依賴程序員編寫的非正式規(guī)范。可執(zhí)行過(guò)程規(guī)范可以充當(dāng) oracle,從而使自動(dòng)生成的測(cè)試用例更加有效,并且可以避免生成由易于出錯(cuò)的啟發(fā)式方法派生的無(wú)效測(cè)試用例,如第 6 節(jié)所述。
1.2 貢獻(xiàn)
本文記錄的研究工作的主要貢獻(xiàn)是 Jdoctor,它是一種將非正式 Javadoc 代碼注釋轉(zhuǎn)換為可執(zhí)行過(guò)程規(guī)范的方法。這種方法是自然語(yǔ)言解析和模式,詞匯和語(yǔ)義匹配技術(shù)的新穎組合。第二個(gè)貢獻(xiàn)是該方法的開放源代碼實(shí)現(xiàn),該方法可以復(fù)制本文中提到的實(shí)驗(yàn)并執(zhí)行其他實(shí)驗(yàn)。第三個(gè)貢獻(xiàn)是對(duì)該方法的多次實(shí)驗(yàn)評(píng)估,證實(shí)了該方法的準(zhǔn)確性和實(shí)用性,從而減少了自動(dòng)生成的測(cè)試報(bào)告的錯(cuò)誤警報(bào)的數(shù)量。
? 我們通過(guò)實(shí)驗(yàn)評(píng)估了 Jdoctor 將 Javadoc 注釋轉(zhuǎn)換為過(guò)程規(guī)范的準(zhǔn)確性。 Jdoctor 的性能明顯優(yōu)于其他最新方法。然后,我們證明 Jdoctor 不僅準(zhǔn)確,而且在測(cè)試生成領(lǐng)域也很有用。與測(cè)試生成工具(Randoop)集成后,Jdoctor 的規(guī)范可減少誤報(bào)。
2 代碼注釋分析
我們的工作受代碼注釋分析的啟發(fā)并重用了其中的想法。與我們的分析代碼注釋技術(shù)有關(guān)的最接近的工作是@ tComment [50]、ALICS [41]和 Toradocu [24]。 @tComment 使用模式匹配來(lái)確定與參數(shù)空缺有關(guān)的三種前提條件屬性。它可以達(dá)到很高的精度,并且可以召回與模式匹配的注釋,但是這些模式非常狹窄,并且不能一概而論。 ALICS 使用詞性標(biāo)記,然后針對(duì)少量硬編碼名詞和術(shù)語(yǔ)進(jìn)行模式匹配,從而根據(jù)代碼注釋生成過(guò)程前置條件和后置條件。同樣,可概括性一點(diǎn)也不明顯,并且需要為每個(gè)新域進(jìn)行手動(dòng)擴(kuò)展。 Toradocu 是我們最初的工作,因此產(chǎn)生了 Jdoctor。它使用自然語(yǔ)言解析,并使用近似詞典序法匹配將識(shí)別出的名詞和動(dòng)詞與任意程序表達(dá)式和運(yùn)算進(jìn)行匹配。 Toradocu 僅在特殊情況下起作用,這僅占 Javadoc 注釋的一小部分。 @tComment 和 Toradocu 都將其提取的屬性應(yīng)用于測(cè)試生成問(wèn)題。 ALICS 尚未應(yīng)用于任何開發(fā)任務(wù)。
? Jdoctor 旨在結(jié)合并適當(dāng)擴(kuò)展這些技術(shù)中的最佳技術(shù)。模式匹配無(wú)法捕獲 Javadoc 標(biāo)簽中自然語(yǔ)言的表現(xiàn)力和多樣性,即使僅在我們案例研究中的程序上進(jìn)行評(píng)估也是如此。 Jdoctor 與自然語(yǔ)言解析一起對(duì)模式匹配進(jìn)行了補(bǔ)充,ALICS 和 Toradocu 也是如此。但是,與以前的工作不同,Jdoctor 不僅限于特定注釋或規(guī)范的小語(yǔ)法,而且可以根據(jù)要分析的程序中定義的抽象來(lái)表達(dá)規(guī)范。與以前的所有工作不同,Jdoctor 添加了一種新穎的語(yǔ)義相似性概念。這樣處理的注釋使用的術(shù)語(yǔ)盡管在語(yǔ)義上是相關(guān)的,但與代碼中的標(biāo)識(shí)符不同。
? 簡(jiǎn)而言之,Jdoctor 會(huì)為各種過(guò)程行為生成規(guī)范:先決條件(與 Toradocu 不同)、常規(guī)后置條件(與@tComment 和 Toradocu 不同)和特殊后置條件(與@tComment 不同),并且比以前的工作更具通用性。與@tComment 不同,Jdoctor 不需要程序針對(duì)非法輸入遵守特定行為(對(duì)于@tComment,則為與 null 相關(guān)的行為)。與@tComment 和 ALICS 不同,Jdoctor 不僅限于特定注釋或規(guī)范的小語(yǔ)法,而且可以根據(jù)要分析的程序中定義的抽象表示規(guī)范。 Jdoctor 結(jié)合了文本模式匹配和自然語(yǔ)言處理(不同于@tComment),并引入了比 ALICS 和 Toradocu 更先進(jìn),更有效的新技術(shù)。與 ALICS 不同,Jdoctor 規(guī)范是可執(zhí)行的,并且涉及數(shù)據(jù)結(jié)構(gòu),例如數(shù)組和集合以及數(shù)學(xué)表達(dá)式。
3 啟發(fā)性的 Javadoc 例子
Java 開發(fā)人員的標(biāo)準(zhǔn)做法是使用 Javadoc 注釋形式的非正式規(guī)范對(duì)代碼進(jìn)行注釋。 Javadoc 工具會(huì)根據(jù)此類注釋自動(dòng)生成 html 格式的 API 文檔。 Javadoc 注釋由自由格式的文本組成,其中一些前面帶有“標(biāo)簽”:用于前提條件的@param標(biāo)簽以及分別用于常規(guī)后置條件和異常行為的@return和@throws標(biāo)簽。現(xiàn)在,我們介紹一些例子。 從流行的開放源代碼 Java 代碼中摘錄的 Javadoc 注釋的示例,以及 Jdoctor 產(chǎn)生的輸出,以突出顯示此工作的挑戰(zhàn)以及先前工作的局限性。
3.1 前置條件
@param標(biāo)記可表征方法參數(shù)并聲明調(diào)用者必須遵守的前提條件。 考慮以下注釋,該注釋來(lái)自 google Guava 的BloomFilter類。 Jdoctor 將此注釋轉(zhuǎn)換為可執(zhí)行規(guī)范,該規(guī)范顯示在 Javadoc 注釋下方的框中。 這些子句與 Java 條件運(yùn)算符“和”(&&)結(jié)合在一起以形成完整的過(guò)程規(guī)范。
? Jdoctor 使用數(shù)學(xué)表達(dá)式(第二和第三@param注釋)和復(fù)合條件(第三@param注釋)正確處理注釋。 Jdoctor 還了解到,有關(guān)第一個(gè)參數(shù)的注釋未指定任何前提條件,因此不會(huì)產(chǎn)生有關(guān)參數(shù)漏斗的任何說(shuō)明。
3.2 特殊的后置條件
@throws和@exception標(biāo)記表示特殊執(zhí)行的后置條件。 讓我們考慮以下來(lái)自 Apache Commons Collections 庫(kù)的ClosureUtils類的摘錄。
? 第一個(gè)特殊的后置條件很簡(jiǎn)單,并且可以使用最新技術(shù)進(jìn)行處理。 但是,只有 Jdoctor 能夠理解與容器中元素相關(guān)的屬性,如第二條@throws注釋中所示。
? Jdoctor 還處理更復(fù)雜的注釋,例如以下注釋,該注釋來(lái)自 Apache Commons Collections 庫(kù)的CollectionUtils類:
盡管此注釋描述的是無(wú)效條件,但現(xiàn)有技術(shù)無(wú)法產(chǎn)生正確的斷言。 Jdoctor 確定“兩個(gè)集合”均指參數(shù)a和b,“比較器”指參數(shù)c。
3.3 普通后置條件
@return標(biāo)記表示方法定期執(zhí)行的后置條件,并且變化最大,因此最具挑戰(zhàn)性。 這是三個(gè)其他技術(shù)無(wú)法處理的示例。
? 第一個(gè)示例來(lái)自 Apache Commons Collections 庫(kù)中的BagUtils類:
? Jdoctor 使用在類Bag中聲明的常量產(chǎn)生后置條件。 將代碼注釋與要測(cè)試的代碼中的方法或其他代碼元素匹配并不總是那么簡(jiǎn)單,正如以下針對(duì)JGraphT庫(kù)的Graph.addEdge()方法的注釋所說(shuō)明的那樣。
? Jdoctor 推斷“此圖”是指圖實(shí)例本身,并且containsEdge方法可以檢查后置條件。 Jdoctor 正確傳遞兩個(gè)頂點(diǎn)作為此方法的參數(shù)以形成邊。
? 將代碼注釋與代碼元素匹配可能還需要一些語(yǔ)義相似性概念。 從同一個(gè) Graph 類中獲取以下示例。
? 由于 Jdoctor 新穎的語(yǔ)義相似性分析,Jdoctor 推斷“未找到”在語(yǔ)義上與“容器中包含一個(gè)元素”的概念有關(guān)。 @tComment 和 Toradocu 缺乏任何語(yǔ)義相似性分析,而 ALICS 僅支持有限數(shù)量的手動(dòng)定義同義詞。
4 JDOCTOR
Jdoctor 將與構(gòu)造函數(shù)和方法有關(guān)的 Javadoc 注釋轉(zhuǎn)換為可執(zhí)行的 Java 表達(dá)式。它的主要見(jiàn)解是觀察到,自然語(yǔ)言注釋中的名詞傾向于與代碼中的變量或表達(dá)式相對(duì)應(yīng),注釋中的動(dòng)詞/謂詞與操作或方法相對(duì)應(yīng)。 Jdoctor 處理 Javadoc 前置條件(@param)、普通后置條件(@return)和特殊后置條件(@throws或@exception)。
Jdoctor 的工作分為四個(gè)步驟:
(1)文本規(guī)范化(第 4.1 節(jié)):Jdoctor 對(duì) Javadoc @ param、@ return、@ throws和@exception塊標(biāo)記中的文本進(jìn)行預(yù)處理,以準(zhǔn)備分析自然語(yǔ)言。此階段包括幾個(gè)文本轉(zhuǎn)換,以方便后續(xù)步驟。
(2)命題識(shí)別(第 4.2 節(jié)):Jdoctor 使用自然語(yǔ)言解析器來(lái)識(shí)別注釋中每個(gè)子句的命題(主語(yǔ)-謂詞對(duì))。
(3)命題翻譯(第 4.3 節(jié)):Jdoctor 將每個(gè)識(shí)別的命題與 Java 元素(例如表達(dá)式或操作)匹配。此步驟是 Jdoctor 的核心,它依賴于模式,詞匯和語(yǔ)義相似性匹配的組合。
(4)創(chuàng)建規(guī)范:Jdoctor 創(chuàng)建 Java 布爾表達(dá)式,該表達(dá)式對(duì)自然語(yǔ)言 Javadoc 注釋進(jìn)行編碼。它用 Java 代碼替換文本中的每個(gè)主題和謂詞,并遍歷解析樹以創(chuàng)建合法的 Java 代碼,包括方法調(diào)用,操作和布爾連接符(來(lái)自語(yǔ)法連接)。
4.1 文本規(guī)范化
Javadoc 注釋很少是語(yǔ)法上完整的英語(yǔ)句子。例如,他們經(jīng)常缺乏標(biāo)點(diǎn)符號(hào),具有隱含的主語(yǔ)和/或動(dòng)詞,并且將數(shù)學(xué)符號(hào)與英語(yǔ)混在一起。當(dāng)前的 NLP 解析器不能總是處理程序員編寫的 Javadoc 注釋樣式。 Jdoctor 通過(guò)在使用 NLP 解析器之前將其預(yù)處理為語(yǔ)法英語(yǔ)句來(lái)使文本可解析。這個(gè)階段適當(dāng)?shù)財(cái)U(kuò)展了先前的工作,并允許處理更多的 Javadoc 注釋。
? 標(biāo)點(diǎn)符號(hào):Jdoctor 會(huì)在缺少句號(hào)時(shí)添加一個(gè)句號(hào)。它還消除了初始標(biāo)點(diǎn)符號(hào),這些標(biāo)點(diǎn)符號(hào)是由于程序員(錯(cuò)誤地)使用 Javadoc 注釋中的逗號(hào)將參數(shù)或異常名稱與其描述分開而產(chǎn)生的。
? 隱式主語(yǔ):評(píng)論可能引用事先提及的主語(yǔ)。例如,典型的@param注釋是“永遠(yuǎn)不會(huì)為空”。由于 Jdoctor 孤立地分析句子,因此每個(gè)句子都需要一個(gè)明確的主題。對(duì)于@param注釋,Jdoctor 在注釋文本的開頭添加參數(shù)名稱。 Jdoctor 還啟發(fā)式地解析代詞(例如“ it”),將其替換為注釋中最后使用的名詞。
? 隱式動(dòng)詞:某些注釋具有隱式動(dòng)詞,例如“ @ param num,a positive number”。 Jdoctor 會(huì)根據(jù)被認(rèn)為是主語(yǔ)的第一個(gè)名詞是單數(shù)還是復(fù)數(shù)來(lái)添加“is”或“are“。
? 句子不完整:如果沒(méi)有主從句,Jdoctor 會(huì)將從屬?gòu)木滢D(zhuǎn)換為主從句。
? 詞匯標(biāo)準(zhǔn)化:為了適應(yīng)以后的模式匹配,Jdoctor 標(biāo)準(zhǔn)化了與 null、if 和 empty 模式有關(guān)的文本。例如,Jdoctor 將“ non-null”和“ nonnull”標(biāo)準(zhǔn)化為“ not null”。
? 數(shù)學(xué)符號(hào):Jdoctor 將不等式轉(zhuǎn)換為可以解析為形容詞的占位符。例如,如果{@code e} <0,則 Jdoctor 將子句轉(zhuǎn)換為表達(dá)式 e <0,并進(jìn)一步轉(zhuǎn)換為 e is LT0。
4.2 命題識(shí)別給定一個(gè)英語(yǔ)句子,Jdoctor 會(huì)識(shí)別<主語(yǔ),謂語(yǔ)>對(duì),也稱為命題 [14],以及連接命題(如果有)的連接詞或析取詞。從自然語(yǔ)言句子中提取<主語(yǔ),謂語(yǔ)>對(duì)稱為開放信息提取(OIE)[1, 14, 18, 46]。
? Jdoctor 首先執(zhí)行部分 POS 標(biāo)記 [41]。它將參數(shù)名稱標(biāo)記為名詞,將不等號(hào)占位符(例如 LT0)標(biāo)記為形容詞。 Jdoctor 通過(guò) Stanford Parser [34]完成 POS 標(biāo)記過(guò)程,該過(guò)程產(chǎn)生代表輸入語(yǔ)句的語(yǔ)義圖(豐富的分析樹)。語(yǔ)義圖的節(jié)點(diǎn)對(duì)應(yīng)于句子的單詞,邊緣對(duì)應(yīng)于單詞之間的語(yǔ)法關(guān)系。
? Jdoctor 根據(jù)句子結(jié)構(gòu)和圖中編碼的語(yǔ)法角色,識(shí)別構(gòu)成主題的單詞和構(gòu)成謂詞的單詞。更精確地講,給定在語(yǔ)義圖中被標(biāo)記為主題的單個(gè)節(jié)點(diǎn)(即單詞),Jdoctor 通過(guò)訪問(wèn)以主題節(jié)點(diǎn)為根節(jié)點(diǎn)的子圖并收集涉及復(fù)合類型關(guān)系的所有單詞來(lái)識(shí)別完整的主題短語(yǔ)、狀語(yǔ)修飾語(yǔ)、形容詞修飾語(yǔ)、確定語(yǔ)和名詞修飾語(yǔ)。這與 ALICS [41]完全不同,ALICS 僅使用由 Stanford Parser 提供的即用型 POS 標(biāo)記,因此缺少所有其他詞的信息。 Jdoctor 通過(guò)收集具有以下語(yǔ)法關(guān)系的單詞來(lái)識(shí)別謂語(yǔ):助詞、系詞、合詞、直接賓語(yǔ)、開放式補(bǔ)語(yǔ)以及形容詞、否定詞和數(shù)字修飾語(yǔ)。
? Jdoctor 從簡(jiǎn)單句子中提取一個(gè)命題,從多子句中提取多個(gè)命題。通過(guò)處理語(yǔ)義圖中諸如“和”和“或”之類的語(yǔ)法連接的專用邊緣,Jdoctor 可以正確支持多子句。在遍歷圖時(shí),Jdoctor 會(huì)確定與正確的布爾連詞或析取詞相互關(guān)聯(lián)的命題,這些命題反映了輸入句子中的語(yǔ)法連詞。
4.3 命題翻譯
Jdoctor 通過(guò)連續(xù)應(yīng)用模式匹配(第 4.3.1 節(jié))和詞匯匹配(第 4.3.2 節(jié))的補(bǔ)充啟發(fā)式方法并結(jié)合語(yǔ)義相似性分析(第 4.3.3 節(jié))來(lái)翻譯每個(gè)命題。 Jdoctor 使用第一個(gè)能翻譯成功的方法。
? 本節(jié)描述單個(gè)命題的翻譯。 Jdoctor 通過(guò)根據(jù)輸入注釋中的語(yǔ)法連接(“或”,“和”)合并組件命題的翻譯來(lái)處理多個(gè)命題。
? 算法 1 顯示 Jdoctor 如何處理 Javadoc 注釋的(規(guī)范化)文本。文本可能包含多個(gè)命題。該算法獨(dú)立地翻譯每個(gè)命題。然后,將這些翻譯(它們是 Java 表達(dá)式和操作)重新組合以創(chuàng)建完整的可執(zhí)行規(guī)范。對(duì)@return注釋會(huì)進(jìn)行特殊重組。 Jdoctor 首先確定 guard、true 屬性和 false 屬性。例如,Apache Commons Collections 中ArrayStack.search()的返回注釋是“對(duì)象堆棧中從 1 開始的深度,如果找不到,則為-1”。 Jdoctor 將“如果未找到”標(biāo)識(shí)為 guard 對(duì)象,將“從對(duì)象的堆棧開始的基于 1 的深度”標(biāo)識(shí)為 true 屬性,即,當(dāng) guard 為 true 時(shí)保留的屬性,而將“ -1”標(biāo)識(shí)為 false 屬性,即將 guard 評(píng)估為 false 時(shí)保留的屬性。
? 將命題轉(zhuǎn)換為 Java 表達(dá)式時(shí)(第 15 行),Jdoctor 嘗試匹配每個(gè)主題并斷言為代碼元素。從直覺(jué)上講,名詞對(duì)應(yīng)于對(duì)象,在源代碼中以表達(dá)式表示對(duì)象,而動(dòng)詞對(duì)應(yīng)于動(dòng)作或謂詞,在源代碼中以操作符表示。
? Jdoctor 首先分析命題的主題(第 17 行),然后嘗試將該主題與代碼元素進(jìn)行匹配,該代碼元素可以是方法的參數(shù),類的字段或類本身。 Jdoctor 檢索范圍內(nèi)所有代碼元素的標(biāo)識(shí)符和類型作為候選者,并尋找最佳匹配。例如,當(dāng)在第 3 節(jié)中處理注釋“如果比較器為空”時(shí),Jdoctor 將主題“比較器”與類型為Comparator的方法的參數(shù)匹配。 Jdoctor 通過(guò)詞法匹配來(lái)實(shí)現(xiàn)此任務(wù),我們將在 4.3.2 節(jié)中對(duì)此進(jìn)行說(shuō)明。
如果 Jdoctor 找到主題的匹配表達(dá)式,它將繼續(xù)尋找相應(yīng)的匹配謂詞(例如示例中的“ is null”)。更詳細(xì)地講,Jdoctor 檢索主題范圍內(nèi)所有公共方法和字段的代碼標(biāo)識(shí)符,以作為可能的候選者。例如,在將主題比較器匹配為Comparator類型之后,Jdoctor 檢索公共方法的完整列表和Comparator類的字段作為可能的候選對(duì)象。一旦確定了可能的候選者,Jdoctor 就會(huì)逐步利用一組啟發(fā)式方法來(lái)推斷可能候選者中謂詞與代碼元素的正確匹配:(i)檢查謂詞是否與一組預(yù)定義翻譯(行)匹配 22),(ii)查找詞匯相似的匹配項(xiàng)(第 24 行),(iii)根據(jù)語(yǔ)義相似性搜索匹配項(xiàng)(第 27 行)。
? 4.3.1模式匹配。 Jdoctor 使用模式匹配將常見(jiàn)短語(yǔ)(例如“是正”,“是負(fù)”和“是空”)映射到 Java 表達(dá)式片段> 0,<0 和== null。與以前的工作一致 [41, 51, 55],Jdoctor 采用了一組可擴(kuò)展的模式,這些模式涵蓋了原始類型、字符串和無(wú)效性檢查的屬性。模式匹配可以有效地轉(zhuǎn)換常見(jiàn)模式,但不能處理特定于域的概念或特定于代碼的行話,例如“如果在圖中找不到頂點(diǎn)”中的概念“頂點(diǎn)”和“圖形”。
? 4.3.2詞法匹配。 Jdoctor 會(huì)根據(jù)直覺(jué)認(rèn)為 Javadoc 注釋中的單詞在詞法上類似于代碼元素,從而嘗試將主題或謂詞與相應(yīng)的代碼元素進(jìn)行匹配。 Jdoctor(i)根據(jù)駝峰案例慣例將代碼候選詞標(biāo)記化為單獨(dú)的術(shù)語(yǔ),(ii)計(jì)算每個(gè)詞與主題/謂詞中每個(gè)單詞之間的 Levenshtein 距離,(iii)選擇 Levenshtein 距離最小的候選詞,只要不超過(guò)閾值即可(默認(rèn)閾值非常小(即兩個(gè)),以盡可能避免錯(cuò)誤匹配)。例如,在處理第 3 節(jié)中出現(xiàn)的注釋“如果比較器為空”時(shí),Jdoctor 將主題“比較器”與方法類型為距離為 0 的比較器的參數(shù)進(jìn)行匹配。
Jdoctor 使用詞法匹配的方式類似于 Toradocu [24]。這項(xiàng)技術(shù)優(yōu)于簡(jiǎn)單的模式匹配(例如@tComment 實(shí)現(xiàn)的模式匹配)和簡(jiǎn)單的自然語(yǔ)言處理(例如 ALICS 中實(shí)現(xiàn)的模式)。它的局限性是基于這樣的假設(shè),即 Javadoc 注釋中的名詞應(yīng)與代碼中的標(biāo)識(shí)符相似,但實(shí)際上并不總是如此。
? 4.3.3語(yǔ)義匹配。為了說(shuō)明模式和詞法匹配的限制,請(qǐng)考慮謂詞“在圖中未找到”。所需的轉(zhuǎn)換為!graph.containsVertex(vertex)。模式匹配僅在有特定模式可用于處理這種情況時(shí)才有效。詞法匹配失敗,因?yàn)榇a元素containsVertex在詞法上與注釋中出現(xiàn)的“在圖中找不到”相近。 Jdoctor 語(yǔ)義匹配方法建立在以下觀察的基礎(chǔ)上,即語(yǔ)法上不同的術(shù)語(yǔ)可能具有緊密的語(yǔ)義。例如,代碼中的方法containsVertex和注釋中的“在圖形中找不到”的概念在詞法上是不同的,盡管它們的語(yǔ)義是相關(guān)的。由于單詞之間的語(yǔ)義相似性,Jdoctor 能夠處理模式匹配和詞法匹配都失敗的情況。 Jdoctor 使用單詞嵌入,這已被證明是表示語(yǔ)義單詞關(guān)系的強(qiáng)大方法。它將單詞嵌入到高維向量空間中,以使單詞之間的距離與語(yǔ)義相似性緊密相關(guān),而與語(yǔ)法差異無(wú)關(guān)。 Jdoctor 為此使用了兩層神經(jīng)網(wǎng)絡(luò)模型 GloVe。
? Jdoctor 從謂詞中刪除自定義的停用詞列表,并在使用 GloVe 計(jì)算語(yǔ)義相似度之前應(yīng)用詞形還原。詞形還原將術(shù)語(yǔ)轉(zhuǎn)換為它們的原形,例如,“playing”和“played”變?yōu)?ldquo;play”,以減少域中術(shù)語(yǔ)的數(shù)量來(lái)簡(jiǎn)化語(yǔ)義匹配。默認(rèn)情況下,Jdoctor 使用停用詞列表,其中包括屬于 Java 語(yǔ)言的冠詞和單詞,例如“ for”,“ do”和“ null”。但是,Glove 模型本身只能捕獲單個(gè)術(shù)語(yǔ)的語(yǔ)義相似性。因此,它將報(bào)告語(yǔ)義上相關(guān)的術(shù)語(yǔ)“頂點(diǎn)”和“圖形”。但是,大多數(shù)情況下,謂詞和代碼標(biāo)識(shí)符由多個(gè)單詞組成。例如,在JGraphT中,注釋摘錄“找到了頂點(diǎn)”應(yīng)與方法containsVertex相匹配。為了一次比較多個(gè)單詞,Jdoctor 使用了單詞移動(dòng)器的距離(WMD)算法 [30]。
? WMD 測(cè)量?jī)蓚€(gè)文本片段之間的語(yǔ)義距離,因?yàn)樽⑨屩械乃袉卧~(在這種情況下為[vertex,is, found])必須與代碼元素標(biāo)識(shí)符中的單詞(在這種情況下為[contain,vertex])中的單詞完全相同。與 Jdoctor 進(jìn)行詞法匹配時(shí)類似,它會(huì)選擇給定閾值內(nèi)語(yǔ)義距離最接近的候選詞。
? 盡管提供了不同的匹配策略,但 Jdoctor 僅對(duì)主語(yǔ)匹配使用詞法相似性。這種方法迫使 Jdoctor 將主語(yǔ)與具有很高精確度的代碼元素進(jìn)行匹配(盡管它可能會(huì)丟失一些匹配項(xiàng))。這種保守的決定對(duì)于 Jdoctor 的性能至關(guān)重要,因?yàn)橹髡Z(yǔ)匹配為以后匹配謂詞提供了范圍。更廣泛的(可能是錯(cuò)誤的)范圍將使謂詞匹配的搜索指向完全錯(cuò)誤的路徑。
? Jdoctor 生成單個(gè) Java 布爾條件作為@param注釋的翻譯,并生成一對(duì)“預(yù)期的異常類型,Java 布爾條件”作為@throws注釋的翻譯。 @return注釋的翻譯不是 Java 的布爾值;取而代之的是,單個(gè)轉(zhuǎn)換由對(duì)應(yīng)于 guard、true 和 false 屬性的三個(gè) Java 布爾條件組成。
5 評(píng)估:翻譯準(zhǔn)確性
我們通過(guò)回答以下研究問(wèn)題來(lái)評(píng)估 Jdoctor 的翻譯準(zhǔn)確性。
? RQ1 在將 Javadoc 注釋轉(zhuǎn)換為過(guò)程規(guī)范時(shí),Jdoctor 的有效性(準(zhǔn)確率和召回率)如何?
? RQ2 Jdoctor 的有效性與最新方法@tComment 和 Toradocu 相比如何?
? 我們根據(jù)準(zhǔn)確率和召回率(信息檢索任務(wù)的標(biāo)準(zhǔn)指標(biāo))來(lái)衡量有效性,例如將 Javadoc 注釋翻譯為過(guò)程規(guī)范。
? 準(zhǔn)確率衡量正確輸出相對(duì)于缺失和錯(cuò)誤輸出的比例。當(dāng) Jdoctor 產(chǎn)生與預(yù)期規(guī)范匹配的規(guī)范時(shí),輸出正確(C)。當(dāng) Jdoctor 不產(chǎn)生任何規(guī)范時(shí),輸出丟失(M)。當(dāng) Jdoctor 在沒(méi)有預(yù)期的規(guī)格(W1)或與預(yù)期的規(guī)格不匹配的規(guī)格(W2)產(chǎn)生規(guī)格時(shí),輸出錯(cuò)誤。準(zhǔn)確率定義為正確輸出數(shù)與輸出總數(shù)之間的比率:
? 召回率衡量工具產(chǎn)生的期望輸出的比例,它定義為正確輸出的數(shù)量與期望輸出的總數(shù)之間的比率:
? 我們的評(píng)估保守地認(rèn)為部分正確的翻譯是錯(cuò)誤的。 例如,如果注釋為“@throws Exception如果x為負(fù)或y為空”,則翻譯“ x <0”被視為錯(cuò)誤。
5.1 實(shí)驗(yàn)設(shè)置
為了進(jìn)行評(píng)估,我們選擇了 6 個(gè)維護(hù)良好的開源 Java 系統(tǒng)(請(qǐng)參閱表 1)。對(duì)于每個(gè)系統(tǒng),我們(i)丟棄沒(méi)有文檔或文檔數(shù)量有限的類,即 Javadoc 注釋少于 5 個(gè),以及(ii)忽略注釋了從java.lang.Object繼承的方法、getter 和 setter(其名稱為方法的注釋)以“ get”或“ set”開頭)。表 1 中的“文檔班級(jí)”列報(bào)告了滿足每個(gè)學(xué)科這些條件的班級(jí)數(shù)。
? 然后,我們選擇并手動(dòng)分析了至少 10%的已記錄類和方法:分別在“已分析類”和“已分析方法”列中。為此,我們應(yīng)用了 PPS(概率正比于規(guī)模)抽樣方法,選擇每個(gè)類別的概率與該類別中的方法數(shù)量成正比。對(duì)于每個(gè)選定類中的每個(gè)分析方法,我們手動(dòng)確定其基本事實(shí)-將 Javadoc 注釋正確轉(zhuǎn)換為可執(zhí)行方法規(guī)范。為此,我們閱讀了用英語(yǔ)表達(dá)的 Javadoc 注釋,并編寫了與每個(gè)@ param、@ return、@ throws和@exception標(biāo)記相對(duì)應(yīng)的可執(zhí)行規(guī)范。至少有兩位作者獨(dú)立審查了每種翻譯。
? 有時(shí),與 Javadoc 標(biāo)記對(duì)應(yīng)的文本不能表示為可執(zhí)行規(guī)范。一個(gè)示例是“如果在讀取文件時(shí)遇到問(wèn)題,則@throws IOException”。對(duì)于此類注釋,我們不希望 Jdoctor 產(chǎn)生任何輸出。我們丟棄了不包含任何可轉(zhuǎn)換為可執(zhí)行規(guī)范的注釋的類,即不屬于已分析類的列表。這給我們留下了 118 個(gè)分析過(guò)的類和 829 個(gè) Javadoc 注釋,我們?yōu)樗鼈兪謩?dòng)生成了真實(shí)的可執(zhí)行規(guī)范。我們的實(shí)驗(yàn)比較了三種工具,所有這些工具均由程序員編寫的非正式英語(yǔ)規(guī)范來(lái)創(chuàng)建可執(zhí)行過(guò)程規(guī)范。
l @tComment [50]與三種不同類型的無(wú)效性規(guī)范的預(yù)定模板匹配。我們希望使用@tComment 實(shí)現(xiàn),但是由于文檔數(shù)量有限而無(wú)法使用(例如,其文件格式未記錄)。我們發(fā)現(xiàn),更容易根據(jù)發(fā)布的描述重新實(shí)現(xiàn)@tComment [50]。我們實(shí)現(xiàn)的@tComment 取得了與本文相似的結(jié)果,并公開供外部檢查。
l Toradocu [24]通過(guò) NLP 和字符串匹配的組合,從@throws 注釋中生成特殊的后置條件。我們使用了來(lái)自 GitHub 的 Toradocu 實(shí)現(xiàn)。
l Jdoctor 是本文描述的工具。我們沒(méi)有在比較中考慮 ALICS [42],因?yàn)榧词乖谧髡叩闹С窒拢覀円矡o(wú)法使該工具正常運(yùn)行(有關(guān)此問(wèn)題的更多信息,請(qǐng)參見(jiàn)第 8 節(jié))。而且,ALICS 沒(méi)有產(chǎn)生可執(zhí)行的規(guī)范,因此很難進(jìn)行比較。
5.2 準(zhǔn)確度結(jié)果 (RQ1, RQ2)
表 2 報(bào)告了@ tComment、Toradocu 和 Jdoctor 在表 1 的主語(yǔ)類上的準(zhǔn)確性。如第 2 節(jié)所述,Toradocu 不處理先決條件,Toradocu 和@tComment 都不處理正常的后置條件(表中的值 n.a. )。表格中的數(shù)據(jù)表明,Jdoctor 的精度可與最新方法相媲美,而 Jdoctor 的召回率遠(yuǎn)高于最新方法。
? 前置條件。 Jdoctor 處理不同類型的前置條件,而@tComment 僅處理通過(guò)簡(jiǎn)單分析處理的與 null 相關(guān)的檢查。 @tCom 的精度比 Jdoctor 更高,這得益于其模式的特殊性,例如“may be null”,“must not be null”和“ @throws IllegalArgu.mentException if...is null”。但是,對(duì)@tComment 的簡(jiǎn)單分析會(huì)錯(cuò)過(guò)許多翻譯,導(dǎo)致召回率比 Jdoctor 低得多。
? 正常的后置條件。 Jdoctor 是唯一足以表達(dá)@return注釋的方法。由于常見(jiàn)返回條件的復(fù)雜性,@return注釋的精度和召回率均低于其他標(biāo)記。例如,注釋“ @return 排序了的數(shù)組”聲明后置條件為檢查例程返回的數(shù)組是否已排序。該檢查涉及對(duì)陣列的循環(huán),在 Jdoctor 的當(dāng)前實(shí)現(xiàn)中不支持此檢查。其他難以檢查的條件是,例如 Commons Math 中的FastMath類中的注釋“ @return n + 1,如果沒(méi)有發(fā)生溢出”,其警衛(wèi)應(yīng)檢查是否發(fā)生了溢出。精度和召回率相對(duì)較低的另一個(gè)原因是后置條件通常包括兩個(gè)或多個(gè)元素之間的比較。目前,Jdoctor 假定主題和謂詞轉(zhuǎn)換為單個(gè) Java 元素,并且無(wú)法處理更復(fù)雜的情況,例如“如果a和c的大小差不等于 1”,“如果最大迭代次數(shù)小于或等于最小迭代次數(shù)”和“如果xval和yval大小不同”。
? 特殊的后置條件。與@tComment 相比,Jdoctor 具有更好的精度和召回率。 @tComment 僅翻譯與空相關(guān)的注釋,即在特定上下文中包含單詞“空”的注釋。例如,Jdoctor 可以翻譯表達(dá)式“ aString 不為空”,而@tComment 則不能。在分析復(fù)雜的句子時(shí),Jdoctor 的句子分析比@tComment 的模式匹配更有效。 Jdoctor 可以翻譯由許多從句與特定的語(yǔ)法連接相聯(lián)系的從句組成的句子,例如x和y為正或z為負(fù),而@tComment 的模式匹配則不然。 Jdoctor 比 Toradocu 具有更好的精度和召回率。 Jdoctor 的更好結(jié)果歸功于更精確的算法,可以將主語(yǔ)和謂詞轉(zhuǎn)換為 Java 元素,并獲得了更多受支持的注釋集(請(qǐng)參見(jiàn)第 4 節(jié))。
? 92%的整體精度和 83%的召回率支持對(duì) RQ1 的肯定回答:Jdoctor 在將 Javadoc 注釋轉(zhuǎn)換為過(guò)程規(guī)范方面有效且準(zhǔn)確。我們的評(píng)估還支持對(duì) RQ2 的肯定回答:與最先進(jìn)的技術(shù)@tComment 和 Toradocu 相比,Jdoctor 的準(zhǔn)確率更高。
5.3 不一致的規(guī)范
通過(guò)檢查每個(gè) Javadoc 注釋的 Jdoctor 輸出,我們手動(dòng)檢查了第 5.2 節(jié)中報(bào)告的 Jdoctor 的準(zhǔn)確性。檢查表明 Jdoctor 可以產(chǎn)生正確但不一致的規(guī)范。盡管目前只能通過(guò)手動(dòng)分析 Jdoctor 的輸出來(lái)找到許多這些問(wèn)題,但我們計(jì)劃擴(kuò)展該工具以自動(dòng)報(bào)告它們。現(xiàn)在,我們描述我們遇到的許多不一致之處。我們發(fā)現(xiàn),許多開發(fā)人員編寫的規(guī)范在邏輯上是不一致的-也就是說(shuō),它們無(wú)法實(shí)現(xiàn)或不能被客戶端使用。這突出了將非正式的英語(yǔ)規(guī)范轉(zhuǎn)換為可執(zhí)行形式的附帶好處:可執(zhí)行形式中的不一致性比非正式表述并不明顯,并且可以自動(dòng)檢查機(jī)器可讀的規(guī)范。下面我們報(bào)告 Jdoctor 可能突出顯示的六類不一致和錯(cuò)誤。
? 一些規(guī)范會(huì)轉(zhuǎn)換為并非總是定義良好的表達(dá)式,因?yàn)樗鼈兊那笾悼赡軙?huì)引發(fā)異常。例如,當(dāng) arg 為 null 時(shí) arg.f> 0,這可能導(dǎo)致兩種合理但不同的解釋:arg.f 表達(dá)式始終具有一個(gè)值(即 arg!= null && arg.f> 0)或條件只要 arg.f 具有值(即 arg!= null || arg.f> 0),則為 true。兩種解釋都在實(shí)踐中出現(xiàn),盡管它們的含義對(duì)于客戶和實(shí)施者而言都大相徑庭。
? 許多規(guī)范具有沖突的前提條件和后置條件。 Commons Collections 中的CollectionUtils類的一個(gè)簡(jiǎn)單示例是
l @param a第一個(gè)集合,不能為null
l @throws NullPointerException 如果a為null
@throws子句指的是違反@param子句的情況,導(dǎo)致兩種規(guī)則上的解釋,盡管相互矛盾:(i)方法的域是所有值(允許程序員傳遞null),并承諾當(dāng)傳入null時(shí)會(huì)發(fā)生什么,或(ii)該域都是非空值,程序員不應(yīng)傳遞空值。兩種解釋都是合理的,并且沒(méi)有辦法知道設(shè)計(jì)者的意圖,但兩者之間的區(qū)別是微不足道的:前者指出,維護(hù)者不得在將來(lái)更改實(shí)施,而后者則規(guī)定實(shí)施者可以在將來(lái)的實(shí)現(xiàn)中自由更改,依賴異常的客戶代碼將可能出錯(cuò)。
? 一些規(guī)范指示給定條件的多個(gè)結(jié)果。例如,讓我們考慮
l @throws NullPointerException 如果arg1為null
l @throws IllegalArgumentException 如果arg2為負(fù)數(shù)
如果arg1為null且arg2為 0,則該例程需要同時(shí)拋出NullPointerException和IllegalArgumentException,這在 Java 中是不可能的。沒(méi)有注意到不一致的客戶可能會(huì)遇到意外的行為。這種不一致性的一個(gè)例子是JGraphT的KShortestPaths類。后置條件中也會(huì)出現(xiàn)類似的不一致之處。
? Jdoctor 自動(dòng)識(shí)別 Javadoc 中的一些很可能是由于復(fù)制和粘貼錯(cuò)誤引起的錯(cuò)誤[6]。例如,Guava 19.0 中CharMatcher.matchesNoneOf方法的文檔指出:“如果此匹配器匹配序列中的每個(gè)字符(包括序列為空時(shí)),則返回 true”,同時(shí)應(yīng)指出該方法不匹配序列中的任何字符。 Jdoctor 將拼寫錯(cuò)誤“匹配每個(gè)字符”正確轉(zhuǎn)換為使用方法matchAllOf的 Java 表達(dá)式,并且此斷言在運(yùn)行時(shí)失敗,突出顯示了不正確的 Javadoc。
? 某些過(guò)程的 Javadoc 引用了不正確的形式參數(shù)名稱。常見(jiàn)的原因是 Javadoc 從重寫的實(shí)現(xiàn)中繼承,并伴隨著形式參數(shù)名稱的更改。 HTML API 文檔的讀者會(huì)在 Javadoc 中看到一組名稱,在方法名稱中看到另一組名稱。通常,這種對(duì)應(yīng)是顯而易見(jiàn)的,但并非總是如此,讀者不必推論它。
? Jdoctor 還自動(dòng)在 Javadoc 中報(bào)告了一些拼寫錯(cuò)誤。例如,在 GraphStream 項(xiàng)目的Node類中,Javadoc 錯(cuò)誤地說(shuō)方法可能拋出IndexOutOfBoundException,而不是正確的IndexOutOfBoundsException。 Jdoctor 可能會(huì)報(bào)告問(wèn)題,因?yàn)樗鼪](méi)有在其類路徑中找到類IndexOutOfBoundException。