奚耀國 沈立煒 趙文耘
(復(fù)旦大學(xué)軟件學(xué)院 上海 201203)(復(fù)旦大學(xué)上海市數(shù)據(jù)科學(xué)重點(diǎn)實(shí)驗(yàn)室 上海 201203)
應(yīng)用程序接口(API)是軟件開發(fā)過程中不可或缺的部分,它封裝了類庫和框架的某個(gè)功能,開發(fā)者無須了解其內(nèi)部構(gòu)造只需調(diào)用接口就可以快速地編寫程序。開發(fā)者傾向于在開發(fā)中使用API以避免重復(fù)工作[1]并提高軟件系統(tǒng)的可重用性[2]??蚣芎皖悗焱ǔ褂蒙壈姹镜姆绞絹硖峁┬碌墓δ芑蛐迯?fù)缺陷,此過程經(jīng)常會變更原先提供API。變更的API通常會導(dǎo)致客戶端在升級類庫版本時(shí)無法正常運(yùn)行。為了避免API變更造成客戶端程序無法兼容的情況發(fā)生,類庫開發(fā)人員往往不會直接更改或移除API,而是將變更的API標(biāo)記為棄用,在之后的版本中逐步將其移除[3-4]。在此機(jī)制下,開發(fā)者可以在棄用API被移除之前對其進(jìn)行遷移[5],從而提升程序的功能性、性能與安全性[3]。
由于API文檔經(jīng)常出現(xiàn)缺失遷移信息的情況[3,6-7],開發(fā)者更習(xí)慣于在網(wǎng)絡(luò)中搜索替換API及其代碼示例。問答網(wǎng)站Stack Overflow存在很多棄用API的討論帖,其中包含對替換API及其使用方式的介紹。然而由于討論帖數(shù)目較多且開發(fā)者的問題描述經(jīng)常與遷移棄用API的詞匯存在差異,因此在尋找替換API及其代碼示例的過程中會耗費(fèi)大量時(shí)間,搜索結(jié)果往往也不夠全面。盡管現(xiàn)有的技術(shù)可以幫助開發(fā)者快速搜索代碼示例[8-9],但搜索結(jié)果包含很多開發(fā)者不關(guān)心的元素,使得代碼可讀性差。同時(shí),這些技術(shù)并未對搜索結(jié)果進(jìn)行篩選排序,開發(fā)者仍需花費(fèi)時(shí)間從搜索結(jié)果中篩選具有參考價(jià)值的代碼示例。
針對上述問題,本文提出基于問答平臺的棄用API遷移建議推薦技術(shù),該技術(shù)旨在通過快速提供包含與文本描述和代碼示例的遷移建議來提高開發(fā)者遷移棄用API的效率。具體而言,該技術(shù)首先結(jié)合棄用API相關(guān)元素、標(biāo)簽和關(guān)鍵詞組成的搜索條件在Stack Overflow中搜索討論帖。在對討論帖進(jìn)行分析之后為回答生成回答快照,回答快照包括文本信息、代碼示例、回答本身的附加信息。之后根據(jù)替換API對回答快照進(jìn)行分類,以快照集的形式表示分類結(jié)果。接著根據(jù)回答快照的附加信息從兩個(gè)維度上對快照集和回答快照進(jìn)行排序。最后依據(jù)替換API對回答快照中的代碼示例進(jìn)行簡化。與現(xiàn)有的遷移技術(shù)相比,該技術(shù)的優(yōu)勢有:1) 開發(fā)者無須提供替換API即可得到包含文本描述和代碼示例的遷移建議;2) 生成的遷移建議將會以特定的規(guī)則進(jìn)行排序,以方便開發(fā)者參考;3) 為了幫助開發(fā)者更快地掌握替換API的使用方式,該技術(shù)會根據(jù)替換API移除代碼示例中無關(guān)的元素,提供清晰簡潔的代碼示例。本文基于此技術(shù)實(shí)現(xiàn)了遷移建議推薦工具并組織了用戶實(shí)驗(yàn)。實(shí)驗(yàn)結(jié)果表明該工具能夠顯著地提高開發(fā)者遷移棄用API的效率。
手動遷移棄用API需要耗費(fèi)大量的時(shí)間與精力,針對此問題,研究者提出了輔助開發(fā)者遷移棄用API的技術(shù)。Dig等[10]將類庫演化過程中API的變更分為結(jié)構(gòu)變更和行為變更。結(jié)構(gòu)變更表示變更了API對應(yīng)方法的方法簽名,此類變更會導(dǎo)致客戶端程序無法成功編譯執(zhí)行,行為變更表示改變了API的功能。Kapur等[11]開發(fā)了遷移工具Trident,開發(fā)者輸入棄用API和替換API,該工具即可自動掃描項(xiàng)目中的棄用API并將其替換為指定的API。但該工具僅能從文本上對棄用API進(jìn)行替換,并未適配代碼上下文。Strobl等[12]開發(fā)了兩個(gè)工具用來遷移棄用API。Generator可以根據(jù)一些啟發(fā)式規(guī)則生成.xml格式的配置文件,而JaSCUT則根據(jù)配置文件對源碼中的棄用API進(jìn)行遷移。然而該工具僅能遷移棄用API的名稱,無法從更細(xì)粒度的級別進(jìn)行遷移。Henkel等[13]開發(fā)了Eclipse插件Catch!,該工具可以記錄開發(fā)者遷移棄用API的操作,并將這些操作回放到相同的棄用API上。但是該工具需要類庫提供者花費(fèi)額外的時(shí)間錄制腳本,同時(shí)由于代碼上下文不同,回放結(jié)果經(jīng)常不能滿足用戶的需要。Dagenais等[14]開發(fā)了棄用API遷移推薦系統(tǒng)SemDiff,該系統(tǒng)通過分析類庫在標(biāo)記棄用API時(shí)如何適應(yīng)自身的變更來抽取遷移策略。以上方法在一定程度上提升了開發(fā)者遷移棄用API的效率,然而這些方法經(jīng)常受到文檔和類庫代碼質(zhì)量的約束,遷移效果往往不能滿足用戶的需求。
Vásquez等[15]發(fā)現(xiàn)API的變更會引發(fā)Stack Overflow中關(guān)于變更API的討論。Vasilescu等[16]調(diào)研了問答網(wǎng)站對開發(fā)者的影響,結(jié)果表明盡管參與問答過程會花費(fèi)時(shí)間,但是卻能夠在一定程度上提升開發(fā)者的開發(fā)效率。Rahman等[9]開發(fā)了代碼搜索插件RACK,該插件結(jié)合IDE中代碼上下文自動從Stack Overflow和Github中搜索代碼示例。Gu等[17]利用深度學(xué)習(xí)將API序列與自然語言關(guān)聯(lián)起來,在開發(fā)者搜索時(shí)可以為其推薦包含相關(guān)API序列的代碼示例。李宇琨等[8]將Stack Overflow中的問答信息對應(yīng)到相關(guān)代碼元素上,再利用代碼元素從Github中搜索代碼示例。
與上述研究相比,本文方法可以在開發(fā)者不指明替換API的情況下為其提供替換API的描述和代碼示例。同時(shí),為了便于開發(fā)者參考,結(jié)果將以特定的規(guī)則進(jìn)行排序。此外,該方法會根據(jù)替換API簡化代碼示例,使得代碼示例更加清晰。
圖1展示了棄用API遷移建議推薦技術(shù)的方法流程,其中:圓角矩形表示組件;矩形表示輸入與輸出;箭頭上的文字表示組件的輸入與產(chǎn)出。該過程以棄用API的相關(guān)信息為輸入,經(jīng)過問答帖收集器、快照生成器、分類器和優(yōu)化器等組件的加工步驟后,生成遷移建議。
圖1 棄用API遷移建議推薦技術(shù)方法流程
棄用API的相關(guān)信息不僅包含棄用API的所屬類、方法名、參數(shù)的類型與數(shù)量,還包括API文檔中的遷移信息。問答帖收集器可以根據(jù)棄用API的元素從Stack Overflow中抽取相關(guān)帖子及其附加信息,并對帖子進(jìn)行篩選過濾,生成回答列表??煺丈善饕蕴囟ǖ囊?guī)則分析帖子中的回答,生成回答快照。回答快照記錄了帖子中某個(gè)回答的關(guān)鍵信息,包括文本信息、代碼示例、該回答的附加信息。分類器可以從替換API的角度對快照進(jìn)行分類,產(chǎn)生不同的快照集。最后,優(yōu)化器根據(jù)特定的規(guī)則,參考文檔中的遷移信息,從兩個(gè)維度上對快照進(jìn)行排序。同時(shí)還會對回答中的代碼示例進(jìn)行簡化,移除無意義的語句或表達(dá)式,從而使遷移建議更加清晰。
問答帖收集包括搜索和篩選兩個(gè)步驟。搜索步驟是指根據(jù)棄用API和關(guān)鍵詞組成的搜索條件從Stack Overflow中搜索相關(guān)問答帖的過程。篩選步驟的目的是去除搜索結(jié)果中的重復(fù)帖子或與目標(biāo)棄用API無關(guān)的帖子。
Stack Overflow提供了不同類型的搜索功能,用戶可以從不同的維度上搜索相關(guān)問題,例如標(biāo)簽、回答、用戶、投票數(shù)、是否有被接受的回答、關(guān)鍵詞等維度。本文工作以“[deprecated]+{alternative deprecate…}+{棄用API名稱}”作為搜索模板,根據(jù)棄用API填充搜索模板生成搜索條件,調(diào)用Stack Exchange提供的接口從Stack Overflow中搜索問答帖,之后經(jīng)過篩選過濾選取足夠數(shù)量的回答。
在搜索問答帖的過程中經(jīng)常會遇到搜索結(jié)果太少或結(jié)果中排序靠后的帖子相關(guān)性不高的情況。針對這一問題,本文結(jié)合Stack Overflow的某些特征來改善搜索策略。Stack Overflow會為相似問題的帖子打上“[duplicate]”標(biāo)記。同時(shí)會在問題的開始部分展示與該問題相似的帖子鏈接。利用這一特征可以收集到更多的問答帖,在搜索帖子時(shí),如果發(fā)現(xiàn)標(biāo)題中有該標(biāo)記,則同時(shí)收集此帖子中指示的相似問答帖。除此之外,本文還通過變更搜索條件來擴(kuò)大搜索范圍,此方式稱為降維搜索,即刪減部分關(guān)鍵詞或去除搜索條件中的“[deprecated]”標(biāo)簽來重新組織搜索條件。
搜索結(jié)果中包含一些不相關(guān)的問答帖,例如非Java語言或討論內(nèi)容與輸入的棄用API不一致的帖子。因此需要對搜索結(jié)果進(jìn)行過濾。此過程從兩個(gè)方面進(jìn)行,一方面去除無關(guān)問答帖,只保留與棄用API有關(guān)的帖子,另一方面移除帖子中低質(zhì)量的回答。
篩選問答帖的規(guī)則如下:1) 移除沒有回答的討論帖。遷移建議主要從回答中產(chǎn)生,因此只考慮有回答的討論帖。2) 篩選Java語言(包括安卓)相關(guān)的討論帖。本文工作提出的技術(shù)只適用于Java語言,暫不考慮對其他語言的支持。3) 篩選與輸入棄用API相關(guān)的討論帖。盡管討論其他API的帖子可能存在一些有用的信息,但這種情況很少出現(xiàn),故排除此情況。具體操作是首先移除回答數(shù)為0的帖子,之后通過判斷問答帖標(biāo)簽中是否包含與編程語言有關(guān)的標(biāo)簽來確定帖子關(guān)聯(lián)的編程語言,若沒有此類標(biāo)簽則從標(biāo)題和文本中提取編程語言關(guān)鍵詞,移除非Java語言的帖子。最后通過文本匹配的方式在帖子的標(biāo)題和正文中匹配棄用API的名稱,將匹配失敗的帖子從列表中移除。
篩選回答的規(guī)則是將投票數(shù)少于0且沒有代碼示例的回答從列表中移除。具體操作是根據(jù)帖子得到回答的ID列表,再根據(jù)ID列表依次得到回答的信息,例如文本信息、投票數(shù)或回答用戶。在回答的文本中匹配
標(biāo)簽,帖子中的代碼示例都是用此標(biāo)簽包裹的,若沒有此標(biāo)簽則證明該回答中不包括代碼示例。將上述兩個(gè)條件都不滿足的回答從帖子中刪除,之后再次判斷各個(gè)帖子的回答數(shù)重新篩選問答帖。在問答帖收集的過程中需要記錄帖子的相關(guān)信息,例如帖子在搜索結(jié)果中的順序、投票數(shù)、回答數(shù)、回答的投票數(shù)、回答有無代碼示例。這些信息將作為對遷移建議進(jìn)行排序的依據(jù)。
3.2 回答分析與快照生成
快照生成器可以分析帖子中的回答并生成回答快照。回答快照包括文本部分、代碼部分、附加信息。文本部分指回答文本中包含方法調(diào)用或代碼元素的語句。這些語句通常包含著對棄用API的替換建議,甚至是對替換API使用方法的介紹。代碼部分指與遷移建議有關(guān)的Java代碼示例。附加信息指回答的額外信息,例如回答中包含的API集合、回答所屬帖子的相關(guān)信息、回答本身的相關(guān)情況等信息。
文本部分由回答中包含方法調(diào)用的語句組成。對于回答中的文本信息,首先以句子為單位將文本分割成語句列表。語句列表中的語句除了包含純本文之外,還包括HTML標(biāo)簽,例如、等。接著依次對語句列表中的語句進(jìn)行分析。對于任意語句,如果符合任意以下規(guī)則則屬于文本部分:1) 語句中包含方法調(diào)用。使用表示方法調(diào)用的正則表達(dá)式進(jìn)行匹配,如果匹配成功則表示語句中包含方法調(diào)用。2) 語句中包含標(biāo)簽。以包裹的單詞表示代碼元素(例如方法調(diào)用或類型名稱),這些元素通常代表了可供選擇的替換建議。3) 被標(biāo)簽包裹的語句。此類型的語句通常是引用API文檔或其他官方說明的語句。4) 語句中存在超鏈接。在語句中通常以超鏈接的形式表達(dá)關(guān)鍵信息,如果該超鏈接不是URL則將其認(rèn)定為關(guān)鍵信息。5) 語句中存在replaced with/by等表示替換的單詞或短語。該步驟的實(shí)現(xiàn)方式是首先判斷語句中是否包含或標(biāo)簽,如果包含則屬于文本部分。之后判斷語句中是否包含非URL的超鏈接,如果包含滿足條件的超鏈接,則此語句屬于文本部分。接著使用表示方法調(diào)用的正則表達(dá)式匹配語句文本,若匹配成功則此語句屬于文本部分。最后判斷語句中是否存在表示替換含義的單詞或短語,若存在這些單詞或短語則此語句屬于文本部分。
代碼部分由回答中所有的Java代碼片段組成。Stack Overflow中使用
標(biāo)簽組合包裹代碼片段。一個(gè)回答中可能包含多個(gè)代碼片段且部分代碼片段可能與替換API無關(guān),例如由其他語言編寫的代碼或不包含API的代碼。對于任意代碼片段,如果符合以下規(guī)則則屬于代碼部分:1) 代碼由Java語言編寫。2) 至少包含一個(gè)方法調(diào)用語句。3) 內(nèi)容不是Exception輸出信息。附加信息是對回答進(jìn)行排序的重要依據(jù),包括回答包含的API集合、回答所屬的帖子、回答所屬帖子在搜索結(jié)果中的排序、回答的時(shí)間、是否被接受和投票數(shù)等信息。API集合由文本部分和代碼部分中出現(xiàn)過的方法調(diào)用組成。附加信息中的其他部分可以很容易地通過解析HTML文本獲得。
圖2展示了Stack Overflow中關(guān)于遷移棄用API CharMatcher.javaDigit()的回答。該回答中回答者引用了官方文檔對該棄用API的解釋,同時(shí)提出了遷移該棄用API的建議,最后給出了替換API的代碼示例。通過上述步驟為該回答生成回答快照,圖中下劃線選中的語句為文本部分,矩形框選中的部分為代碼部分,圖中下半部分展示了分析該回答得到的附加信息。

圖2 回答快照示例
3.3 回答快照的分類
在生成回答快照后,按照附加信息中的API集合對其進(jìn)行分類。API集合中的API可以分為兩種類型:關(guān)鍵API(K_API)和普通API(N_API)。如果某個(gè)API同時(shí)存在于一個(gè)回答的文本部分和代碼部分中,或該API是棄用API文檔的遷移信息中指示的替換API,則此API為關(guān)鍵API。若該API只存在于兩個(gè)快照中的其中一個(gè),則為普通API。
在本文工作中,我們認(rèn)為同時(shí)出現(xiàn)在文本部分和代碼部分中的替換API應(yīng)該是回答者更推薦的API,同時(shí)官方文檔中描述的替換API同樣值得被推薦。因此,為了得到更加精準(zhǔn)、有價(jià)值的替換API,回答快照將根據(jù)關(guān)鍵API進(jìn)行分類?;卮鹂煺盏姆诸惤Y(jié)果是若干個(gè)快照集,快照集的類型由關(guān)鍵API決定,即一個(gè)關(guān)鍵API對應(yīng)一個(gè)快照集。在分類之前,首先收集所有回答快照中的關(guān)鍵API,對關(guān)鍵API進(jìn)行去重后為每一個(gè)關(guān)鍵API生成快照集。在對快照進(jìn)行分類時(shí),依次遍歷API集合中的API。對于任意一個(gè)API,如果有對應(yīng)的快照集,則將該API所屬的回答快照放入對應(yīng)的快照集。對于沒有對應(yīng)快照集的API,則將其忽略。由于回答快照中可能包含多個(gè)API,因此一個(gè)回答快照可能會被放入多個(gè)快照集。
圖3是對回答快照進(jìn)行分類的示意圖。圖中n個(gè)快照中有四個(gè)關(guān)鍵API(A,B,C,D),因此可以分為四類并產(chǎn)生四個(gè)快照集??煺?有五個(gè)API(A,B,C,E,F(xiàn)),其中A、B、C分別能找到對應(yīng)的快照集,可以將快照1放入相應(yīng)的快照集。剩余的兩個(gè)API(E,F(xiàn))無法找到對應(yīng)快照集,因此將它們忽略。其余的快照可以使用相同的規(guī)則將其加入到快照集。

圖3 回答快照分類示意圖
3.4 回答快照的排序
對回答快照進(jìn)行排序可以幫助開發(fā)者更快地確定替換API進(jìn)而完成遷移工作。本文從兩個(gè)維度上對其進(jìn)行排序:1) 對同一個(gè)快照集內(nèi)的回答快照排序。2) 對代表不同替換API的快照集排序。
快照集包含多個(gè)回答快照,可以根據(jù)快照的附加信息對其打分并根據(jù)最后的分值進(jìn)行排序。表1展示了快照集內(nèi)回答快照的評分規(guī)則,并列出了各個(gè)規(guī)則下可能的分值。以下是對評分標(biāo)準(zhǔn)的詳細(xì)介紹。
1) 所屬帖子在搜結(jié)果中的順序。帖子在搜索結(jié)果中越靠前則分值越高。排名最高帖子中的回答評分為5,隨著排名下降,分值依次減1,最低為1分。不同的搜索輪次有不同的權(quán)值。使用完整的搜索條件搜索到的帖子權(quán)值為4,在減少搜索條件后,權(quán)值相應(yīng)減少。刪減部分關(guān)鍵詞權(quán)值為3,移除標(biāo)簽[deprecated]或完全刪除關(guān)鍵詞權(quán)值為2,若只使用棄用API的名稱進(jìn)行搜索則權(quán)值為1。搜索過程中重復(fù)的帖子(即被[duplicate]標(biāo)記的帖子)擁有相同的權(quán)值?;卮鹂煺赵诖瞬糠种械脑u分是權(quán)值與分?jǐn)?shù)的乘積。
2) 回答是否被接受。被提問者接受的回答應(yīng)該有更高的分?jǐn)?shù)。在此部分中,被接受的回答快照的評分為10,其余的回答快照沒有評分。
3) 投票數(shù)。投票數(shù)的高低表明回答被認(rèn)可的程度?;卮鹂煺赵诖瞬糠种械脑u分為獲得投票的數(shù)量。
4) 回答快照的完整性。代碼部分通常比文本部分更具有參考價(jià)值。若快照中包含代碼部分,則評分加2,若包含文本部分,則評分加1。因此,如果回答快照同時(shí)包含上述兩個(gè)部分,則評分為3。
5) 回答下的評論。評論在一定程度上可以反映回答被關(guān)注的程度。若包含兩條以上非作者本人的回答,則評分為3。若除作者以外的評論者大于等于兩人,則評分為5。其余情況在此部分中的評分為1。
6) 回答者的聲望。回答的質(zhì)量與回答者的聲望有關(guān)聯(lián)。若回答者的聲望大于10 000,則評分為3。若回答者的聲望處在1 000至10 000之間,則此回答的評分為2。聲望小于1 000的回答者的回答評分為1。

表1 快照集內(nèi)回答快照的評分規(guī)則
回答快照的最終評分等于以上各部分評分的和。評分高的回答更具備參考價(jià)值。快照集中的回答快照最終將以評分逆序的形式進(jìn)行排序。在此過程中可能會出現(xiàn)評分相同的回答快照,本文根據(jù)回答時(shí)間對評分相同的快照進(jìn)行排序,回答時(shí)間越晚則排名更靠前。
每一個(gè)快照集代表一個(gè)替換API,對快照集進(jìn)行排序可以使開發(fā)者在遷移過程中快速選擇合適的替換API。此步驟同樣以評分的方式對快照集進(jìn)行排序。表2展示了快照集的評分規(guī)則,并列出了各個(gè)規(guī)則下可能的分值。以下是對評分標(biāo)準(zhǔn)的詳細(xì)介紹。
1) 文檔遷移信息中的替換API。文檔中的遷移信息由類庫的維護(hù)者提供,相比其他途徑,從遷移信息中獲得的替換API更能夠滿足開發(fā)者的需求。因此,此過程中文檔提供的替換API對應(yīng)的快照集評分為10。代表其他替換API的快照集沒有評分。
2) 快照集包含的快照數(shù)。快照集中的回答快照數(shù)量反映了對應(yīng)替換API被推薦的程度。包含最多快照數(shù)量的快照集評分為10,隨數(shù)量的降低依次減少1分,最低評分為1分。
3) 快照集中排名前三的快照評分總和?;卮鹂煺盏脑u分已經(jīng)反映了替換API被認(rèn)可的程度,可以參考快照評分對快照集進(jìn)行排序。由于不同快照集包含的回答快照數(shù)量不同,同時(shí)為了避免低質(zhì)量的回答對平均值的影響,此過程中使用每個(gè)快照集中排名前三快照的評分總和作為評分依據(jù)。評分總和最多的快照集評分為10分,隨著評分總和的降低依次減少1分,最低評分為1分。

表2 快照集的評分規(guī)則
與對回答快照進(jìn)行排序的方式相同,此步驟同樣以各部分的評分和作為排序依據(jù),并以評分逆序的形式作為最終結(jié)果。因?yàn)榭煺占臄?shù)量有限,此步驟不需要對評分相同的快照集進(jìn)行額外的排序。
3.5 代碼示例的簡化
回答快照中的代碼示例通常包含著很多API,使得替換API的用法不能被很清晰地呈現(xiàn)出來。為了幫助開發(fā)者快速掌握替換API的使用方法,本工作中會根據(jù)替換API對回答快照中的代碼示例進(jìn)行簡化。
抽象語法樹是代碼分析的基礎(chǔ),該步驟需要為待簡化的代碼示例建立抽象語法樹,以此來分析示例中替換API與上下文的關(guān)系。然而,Stack Overflow中的代碼示例通常是無法單獨(dú)編譯運(yùn)行的代碼片段。傳統(tǒng)的抽象語法樹解析器(例如JavaParser)無法解析這些代碼片段[18]。本文通過正則表達(dá)式從示例中匹配代碼結(jié)構(gòu)并將其組織成抽象語法樹的形式,從而為代碼片段生成抽象語法樹。
代碼示例通常是以方法聲明或語句(Statement)為單位呈現(xiàn)的,因此本文忽略了方法聲明及以上的結(jié)構(gòu)類型,同時(shí)也忽略了不常見的結(jié)構(gòu),例如Lambda表達(dá)式等。表3展示了部分結(jié)構(gòu)類型及相應(yīng)的示例。其中包含常見的選擇結(jié)構(gòu)(if,switch)和循環(huán)結(jié)構(gòu)(while,do-while,for,foreach),也包含了普通的表達(dá)式結(jié)構(gòu),例如變量聲明(VarDeclare)、方法調(diào)用(MethodCall)和賦值表達(dá)式(Assign)。此外還包含一些語句結(jié)構(gòu),例如方法返回值結(jié)構(gòu)(ReturnStmt)和異常處理結(jié)構(gòu)(TryStmt)。除了表3中列出的結(jié)構(gòu),該步驟還對一些更基礎(chǔ)的結(jié)構(gòu)(例如二元表達(dá)式、字符串表達(dá)式等)做了相似的處理。

表3 抽象語法樹的部分結(jié)構(gòu)類型
本文通過分析代碼示例中不同語句與替換API關(guān)系來對代碼示例進(jìn)行簡化。首先從抽象語法樹中定位替換API的使用位置,并從替換API中抽取元素(即API調(diào)用者和參數(shù))。之后遍歷樹中的節(jié)點(diǎn),找到與這些元素相關(guān)的節(jié)點(diǎn)。元素與節(jié)點(diǎn)之間的相關(guān)性分為兩種類型:1) 運(yùn)行該節(jié)點(diǎn)的代碼會導(dǎo)致元素的狀態(tài)發(fā)生改變,這種相關(guān)性稱為寫相關(guān)。2) 該節(jié)點(diǎn)的代碼只是使用元素進(jìn)行了其他的操作,未改變元素的狀態(tài),這種相關(guān)性稱為讀相關(guān)。寫相關(guān)的節(jié)點(diǎn)包括元素被賦值的賦值表達(dá)式、聲明元素的變量聲明語句、元素作為調(diào)用者或參數(shù)的方法調(diào)用。讀相關(guān)的節(jié)點(diǎn)包括元素參與的賦值表達(dá)式或變量聲明等語句。該步驟僅考慮寫相關(guān)的節(jié)點(diǎn),從寫相關(guān)的節(jié)點(diǎn)中分離出會決定這些元素的其他元素。例如變量A是變量B經(jīng)過一系列操作后得到的,則變量B的改變會導(dǎo)致變量A改變,即變量A由變量B決定。重復(fù)上述過程,找到其他元素對應(yīng)的寫相關(guān)的節(jié)點(diǎn),直到找到所有與替換API元素有寫相關(guān)關(guān)系的節(jié)點(diǎn)。最后將這些寫相關(guān)節(jié)點(diǎn)的訪問類型設(shè)置為“WRITE”,從抽象語法樹中移除訪問類型不為“WRITE”的節(jié)點(diǎn),剩余的節(jié)點(diǎn)可以轉(zhuǎn)化為簡化后的代碼示例。簡化結(jié)果取決于替換API,根據(jù)不同的替換API簡化同一段代碼示例會產(chǎn)生不同的結(jié)果。
算法1展示了簡化代碼示例的過程。該算法以rAPI(替換API)和AST(抽象語法樹)為輸入,通過分析后確定AST中與替換API元素具有寫相關(guān)關(guān)系的節(jié)點(diǎn),在修改這些節(jié)點(diǎn)的訪問類型后返回整個(gè)AST。首先根據(jù)替換API從AST中定位對應(yīng)的節(jié)點(diǎn)(第2行),并從該節(jié)點(diǎn)中提取替換API的元素,放置到有序集合elements中(第3行)。依次遍歷elements中的元素,對于每一個(gè)元素,從AST中搜索所有與該元素有寫相關(guān)關(guān)系的節(jié)點(diǎn),并放置到列表nodes中(第5行)。之后遍歷nodes中的節(jié)點(diǎn),從這些節(jié)點(diǎn)中搜索可以決定elements中元素的相關(guān)元素,并將這些元素添加至elements的末尾(第6-第9行)。在添加元素時(shí),elements會過濾重復(fù)的元素,因此不會出現(xiàn)死循環(huán)的情況。最后,將AST中與elements中的任意元素有寫相關(guān)關(guān)系節(jié)點(diǎn)的訪問類型設(shè)置成“WRITE”并返回AST(第11-第16行)。
算法1根據(jù)替換API簡化代碼示例算法
1 function Code_Simplify(rAPI,AST)
2 rNode ← find(rAPI,AST)
3 elements ← extract(rNode)
4 for all e∈elements do
5 nodes ← findNodeByElement(e,AST)
6 for all n∈nodes do
7 relatedElements ← findRelatedElement(n,elements)
8 elements.addAll(relatedElements)
9 end for
10 end for
11 for all n∈AST do
12 if accessWrite(n,elements) then
13 n.accessType ← WRITE
14 end if
15 end for
16 return AST
17 end function
算法1中判斷節(jié)點(diǎn)之間的關(guān)系是通過數(shù)據(jù)流的方式實(shí)現(xiàn)的?,F(xiàn)有技術(shù)無法對代碼片段生成數(shù)據(jù)流圖,因此,本文采用了較為簡單的文本匹配的方式來匹配不同語句中相同的變量,以此為不同節(jié)點(diǎn)中的變量建立數(shù)據(jù)流關(guān)系。該方法建立的數(shù)據(jù)流關(guān)系基本可以滿足確定節(jié)點(diǎn)之間關(guān)系的需求。
圖4展示了根據(jù)替換API簡化代碼示例。圖中左半部分是未簡化過的代碼示例,右半部分是使用上述方法對其進(jìn)行簡化后的代碼示例。該示例中的替換API是Calendar.get()。該代碼示例對應(yīng)的抽象語法樹如圖4所示。其中括號中的數(shù)字表示該節(jié)點(diǎn)對應(yīng)代碼中的行號。在對該代碼示例進(jìn)行簡化時(shí),首先確定替換API的使用位置。從替換API中可以提取出元素cal和Calendar.YEAR,由于參數(shù)Calendar.YEAR是常量,因此只需要對cal進(jìn)行分析。根據(jù)算法1,與cal有關(guān)的節(jié)點(diǎn)對應(yīng)的代碼是第5-第7行。在這幾行中,可以決定cal的元素只有date,對date進(jìn)行分析后得出第3行對應(yīng)的節(jié)點(diǎn)與該元素有寫相關(guān)關(guān)系。最終將第3、第5-第7行代碼對應(yīng)節(jié)點(diǎn)的訪問類型設(shè)置為“WRITE”。圖5中矩形框選中的節(jié)點(diǎn)表示訪問類型為“WRITE”的節(jié)點(diǎn)。將這些節(jié)點(diǎn)翻譯成代碼得到簡化后的代碼示例。

圖4 簡化代碼示例

圖5 代碼示例的抽象語法樹
4 工具與實(shí)驗(yàn)
4.1 工具介紹
基于上文介紹的技術(shù),我們設(shè)計(jì)并開發(fā)了棄用API遷移輔助工具,圖6展示了該工具的用戶界面。該工具是一個(gè)桌面客戶端應(yīng)用,用戶可以很方便地使用該工具對棄用API進(jìn)行遷移。

圖6 棄用API遷移建議推薦工具用戶界面
用戶首先點(diǎn)擊左上角的“打開項(xiàng)目”按鈕,選擇客戶端項(xiàng)目加載到工具中。工具會掃描項(xiàng)目中的Java文件并檢測棄用API,同時(shí)以刪除線的形式標(biāo)記棄用API。用戶點(diǎn)擊棄用API所在行的“遷移”按鈕后,該工具會顯示替換API和遷移建議。替換API列表展示了問答帖中所有回答推薦的替換API。點(diǎn)擊其中的替換API,右下方會顯示對應(yīng)的遷移建議。遷移建議展示了一個(gè)快照集中經(jīng)過排序的回答快照,每個(gè)遷移建議包含所屬問答帖的標(biāo)題、文本信息、代碼示例。用戶可以點(diǎn)擊標(biāo)題右側(cè)的“打開原帖>>”即可跳轉(zhuǎn)至Stack Overflow中該帖子的網(wǎng)頁。此外,用戶點(diǎn)擊“遷移建議”右側(cè)的“只看代碼”即可屏蔽遷移建議的文本信息。
此外,該工具還提供了一些額外的基礎(chǔ)功能,例如打開項(xiàng)目、代碼文本編輯器、訪問外部鏈接和棄用API定位等功能。這些功能可以為開發(fā)者帶來更好的體驗(yàn)。
4.2 實(shí)驗(yàn)準(zhǔn)備
為驗(yàn)證本文方法和技術(shù)的有效性和實(shí)用性,設(shè)計(jì)了用戶實(shí)驗(yàn)。我們選擇了12個(gè)在Stack Overflow中有相關(guān)討論的棄用API,根據(jù)這些API的功能和所屬類庫將它們分為5組。分別為這5組棄用API添加代碼上下文形成5個(gè)可以成功編譯的代碼片段。每個(gè)代碼片段對應(yīng)一個(gè)任務(wù),表4展示了5個(gè)任務(wù)中的棄用API與其所屬的類庫。

表4 實(shí)驗(yàn)任務(wù)介紹
本實(shí)驗(yàn)邀請了10名Java開發(fā)者并將他們分為實(shí)驗(yàn)組和控制組,每組5名實(shí)驗(yàn)者。實(shí)驗(yàn)組的任務(wù)是使用工具為棄用API生成遷移建議,并且參考生成的遷移建議遷移代碼片段中的棄用API??刂平M的任務(wù)是在Stack Overflow中搜索棄用API的相關(guān)討論帖,參考帖子中的討論遷移代碼片段中的棄用API。遷移成功的標(biāo)準(zhǔn)是代碼片段中的棄用API全部被遷移且代碼片段仍然可以被成功編譯。
實(shí)驗(yàn)過程中會記錄參與者完成每個(gè)任務(wù)的耗時(shí),如果參與者完成某個(gè)任務(wù)超過20 min或放棄任務(wù),則將耗時(shí)記錄為20 min。同時(shí),實(shí)驗(yàn)組成員需要對工具生成的遷移建議進(jìn)行評分。評分共分為四個(gè)檔:1分表示生成的遷移建議完全沒有參考價(jià)值,例如生成的遷移建議可讀性差或推薦了完全錯(cuò)誤的替換API。2分表示遷移建議僅推薦了正確的替換API,但參與者無法從文本部分或代碼部分中學(xué)習(xí)替換API的使用方法。3分表示遷移建議推薦了正確的替換API且文本部分和代碼部分具有一定的參考價(jià)值,但參與者仍然需要打開原帖查看原始信息才能完全遷移棄用API。4分表示參與者只需要參考工具生成的遷移建議即可完成遷移任務(wù)。實(shí)驗(yàn)組成員需要對5個(gè)任務(wù)中所有為棄用API生成的遷移建議進(jìn)行評分。
4.3 實(shí)驗(yàn)結(jié)果分析
表5展示了兩組成員在用戶實(shí)驗(yàn)中的耗時(shí)以及實(shí)驗(yàn)組成員對遷移建議評分的平均值、最大值和最小值。從時(shí)間角度來看,實(shí)驗(yàn)組成員在任務(wù)1、2、4中的平均耗時(shí)大約比控制組少2 min左右,原因是控制組成員需要耗費(fèi)額外的時(shí)間去Stack Overflow中尋找相關(guān)討論帖,同時(shí)要總結(jié)多個(gè)討論帖中的內(nèi)容并選擇最合適的替換API。而從評分角度來看,在這三個(gè)任務(wù)中,實(shí)驗(yàn)組成員給分最高的為4分,最低為2分,平均分均在3分以上,說明工具為這三個(gè)任務(wù)中的棄用API生成的遷移建議具有較高的參考價(jià)值。部分參與者在實(shí)驗(yàn)2、實(shí)驗(yàn)4中給了2分,原因是他們認(rèn)為生成的遷移建議過濾掉了原帖對替換API參數(shù)的介紹,導(dǎo)致他們無法直接通過參考代碼示例完成任務(wù)。

表5 用戶實(shí)驗(yàn)結(jié)果
實(shí)驗(yàn)3和實(shí)驗(yàn)5中實(shí)驗(yàn)組成員的平均耗時(shí)與控制組相差不大。兩組成員同時(shí)表示在遷移實(shí)驗(yàn)3中的棄用API Component.mouseDown(Event,int,int)時(shí)耗費(fèi)了大量時(shí)間,甚至部分參與者直接放棄任務(wù)。盡管在Stack Overflow中可以找到關(guān)于該API的討論帖,然而大部分帖子中的回答并未提供替換API及其使用方式。兩組成員僅用帖子中少量的替換API描述信息遷移該API,因此都耗費(fèi)了大量的時(shí)間修改替換API使其符合代碼上下文語法規(guī)范。遷移實(shí)驗(yàn)5中的兩個(gè)棄用API難度較小,且Stack Overflow中關(guān)于兩個(gè)API遷移建議的討論較為集中,回答者的觀點(diǎn)也類似,因此兩組成員在任務(wù)5中的耗時(shí)較少。從評分角度來看,實(shí)驗(yàn)組成員對實(shí)驗(yàn)3的遷移建議評分較低,平均分只有2.5分,表明工具對遷移難度大或Stack Overflow中討論較少的棄用API支持力度不足。實(shí)驗(yàn)組成員對難度較低的實(shí)驗(yàn)5給出了較高的評分,表明該工具對遷移難度較低或帖子討論集中的棄用API具有良好的支持度。
總體而言,該實(shí)驗(yàn)中實(shí)驗(yàn)組成員的表現(xiàn)優(yōu)于控制組。實(shí)驗(yàn)結(jié)果表明,本文提出的方法和工具可以顯著提高客戶端開發(fā)者遷移棄用API的效率。
5 結(jié) 語
本文提出基于問答平臺的棄用API遷移建議推薦技術(shù),該技術(shù)根據(jù)棄用API從問答網(wǎng)站中收集相關(guān)討論帖,對帖子中的回答進(jìn)行過濾、分類、排序、簡化代碼示例等操作后生成可供開發(fā)者參考的遷移建議?;诖思夹g(shù)設(shè)計(jì)并實(shí)現(xiàn)了棄用API遷移輔助工具,實(shí)驗(yàn)結(jié)果表明該工具可以顯著提高開發(fā)者遷移棄用API的效率。
本文的方法仍然具有不足之處。首先,該方法對遷移難度較大或討論較少的棄用API支持度有待提高,之后的工作會結(jié)合自然語言處理技術(shù)來應(yīng)對復(fù)雜的文本結(jié)構(gòu),以此來提供更好的遷移支持。其次,該方法僅能將遷移建議以文本和代碼示例的方式推薦給用戶,不能從這些信息中抽取出可以自動化適配到客戶端的遷移策略,之后的工作中會結(jié)合一些機(jī)器學(xué)習(xí)相關(guān)技術(shù)根據(jù)文本和代碼生成遷移策略,提高方法的智能化程度,進(jìn)一步減少開發(fā)者的工作量。